aotrautils 0.0.1803 → 0.0.1806
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aotrautils/aotrautils.build.js +1496 -1574
- aotrautils/package.json +1 -1
aotrautils/aotrautils.build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (
|
|
3
|
+
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
|
|
4
4
|
/*-----------------------------------------------------------------------------*/
|
|
5
5
|
|
|
6
6
|
|
|
@@ -945,11 +945,8 @@ aotest.run=function(testName=null,scenarioName=null){
|
|
|
945
945
|
}
|
|
946
946
|
});
|
|
947
947
|
|
|
948
|
-
|
|
949
948
|
}
|
|
950
949
|
|
|
951
|
-
|
|
952
|
-
|
|
953
950
|
let currentExecutedTest=window.aotestAllTestsManager[chosenTestParam.name];
|
|
954
951
|
var functionDefinition=currentExecutedTest.functionDefinition;
|
|
955
952
|
|
|
@@ -1068,9 +1065,6 @@ aotest.run=function(testName=null,scenarioName=null){
|
|
|
1068
1065
|
}
|
|
1069
1066
|
}
|
|
1070
1067
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
1068
|
// *****************************************
|
|
1075
1069
|
// We check function/methods calls here :
|
|
1076
1070
|
// (overrides all other previous test result calculations)
|
|
@@ -1133,7 +1127,6 @@ aotest.run=function(testName=null,scenarioName=null){
|
|
|
1133
1127
|
//
|
|
1134
1128
|
// }
|
|
1135
1129
|
|
|
1136
|
-
|
|
1137
1130
|
// Execution measurment end :
|
|
1138
1131
|
scenarioResultObjLocal.global.executionEndTime=getNow();
|
|
1139
1132
|
|
|
@@ -1178,23 +1171,18 @@ aotest.run=function(testName=null,scenarioName=null){
|
|
|
1178
1171
|
}
|
|
1179
1172
|
}
|
|
1180
1173
|
|
|
1181
|
-
|
|
1182
1174
|
// There can be no other case if we are in UI tests case.
|
|
1183
1175
|
}
|
|
1184
1176
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
1177
|
// States comparison test handling :
|
|
1188
1178
|
// (overrides all other previous test result calculations)
|
|
1189
1179
|
if( // (to avoid calculating more if test has already failed :)
|
|
1190
1180
|
!scenarioResultObjLocal.global.isFailed
|
|
1191
1181
|
&& !isChosenScenarioParamArray
|
|
1192
1182
|
&& chosenScenarioParam.after){
|
|
1193
|
-
|
|
1194
1183
|
|
|
1195
1184
|
let beforeResultFlat=currentExecutedTest.oldResultFlat; // expected result
|
|
1196
1185
|
|
|
1197
|
-
|
|
1198
1186
|
// At this point, we already have applied the method to the input object, in order to compare it with the output object, if necessary.
|
|
1199
1187
|
let actualInstanceFlat=(actualInstance!=null?getAsFlatStructure(actualInstance):null); // actual instance (after execution)
|
|
1200
1188
|
let actualArgsFlat=(functionParametersPopulated!=null?getAsFlatStructure(functionParametersPopulated):null); // actual arguments (after execution)
|
|
@@ -1246,17 +1234,13 @@ aotest.run=function(testName=null,scenarioName=null){
|
|
|
1246
1234
|
|
|
1247
1235
|
}
|
|
1248
1236
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
1237
|
// DBG
|
|
1252
1238
|
console.log("³³³³³³ areEquivalentInstances",areEquivalentInstances);
|
|
1253
1239
|
console.log("³³³³³³ areEquivalentArgs",areEquivalentArgs);
|
|
1254
1240
|
console.log("³³³³³³ areEquivalentResults",areEquivalentResults);
|
|
1255
1241
|
|
|
1256
|
-
|
|
1257
1242
|
}
|
|
1258
1243
|
|
|
1259
|
-
|
|
1260
1244
|
// We check sub-scenarii here :
|
|
1261
1245
|
var subScenariiList=null;
|
|
1262
1246
|
if(!isChosenScenarioParamArray)
|
|
@@ -1624,12 +1608,31 @@ aotest.profile=function(rootObject,methodName,visited=[]){
|
|
|
1624
1608
|
|
|
1625
1609
|
window.sleep = (millis) => new Promise( resolve => setTimeout(resolve, millis) );
|
|
1626
1610
|
|
|
1611
|
+
window.PeriodicalExecuter=class PeriodicalExecuter{
|
|
1612
|
+
constructor(callback, refreshRateMillis=1000, instance){
|
|
1613
|
+
this.callback=callback;
|
|
1614
|
+
this.refreshRateMillis=refreshRateMillis;
|
|
1615
|
+
this.instance=instance;
|
|
1616
|
+
const self=this;
|
|
1617
|
+
this.intervalId=setInterval(()=>self.callback.apply(self.instance), this.refreshRateMillis);
|
|
1618
|
+
}
|
|
1619
|
+
stop(){
|
|
1620
|
+
clearInterval(this.intervalId);
|
|
1621
|
+
}
|
|
1622
|
+
restart(){
|
|
1623
|
+
const self=this;
|
|
1624
|
+
this.intervalId=setInterval(()=>self.callback.apply(self.instance), this.refreshRateMillis);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
1627
|
|
|
1628
1628
|
|
|
1629
1629
|
//================================================================
|
|
1630
1630
|
//================= Cryptography utility methods =================
|
|
1631
1631
|
//================================================================
|
|
1632
1632
|
|
|
1633
|
+
// Node dependencies :
|
|
1634
|
+
if(typeof(require)!="undefined" && typeof(sjcl)=="undefined" ) sjcl = require("sjcl");
|
|
1635
|
+
|
|
1633
1636
|
|
|
1634
1637
|
// NOT AOTESTABLE !
|
|
1635
1638
|
//TODO : develop :
|
|
@@ -1977,37 +1980,41 @@ window.nothing=function(nullable, insist=false){
|
|
|
1977
1980
|
return false;
|
|
1978
1981
|
}
|
|
1979
1982
|
|
|
1980
|
-
window.
|
|
1983
|
+
window.getOrCreateEmptyAttribute=function(parentObject, attributeNameOrAttributesNames,
|
|
1984
|
+
// BECAUSE CAUTION : if we have defaultValue as parameter, sometimes because of the recursive call, it can refer to a persisting object ! Thus, creating a strange bug.
|
|
1985
|
+
// (SO WE ALWAYS NEED TO START AS A BLANK STATE FOR DEFAULT VALUE !)
|
|
1986
|
+
forceArrayAttributeDefaultValue=false){
|
|
1981
1987
|
if(nothing(parentObject)) return null;
|
|
1982
|
-
if(nothing(
|
|
1983
|
-
if(isString(
|
|
1984
|
-
const attributeName=(isArray(
|
|
1985
|
-
|
|
1988
|
+
if(nothing(attributeNameOrAttributesNames)) return parentObject;
|
|
1989
|
+
if(isString(attributeNameOrAttributesNames) || (isArray(attributeNameOrAttributesNames) && attributeNameOrAttributesNames.length===1)){
|
|
1990
|
+
const attributeName=(isArray(attributeNameOrAttributesNames) && attributeNameOrAttributesNames.length===1)?
|
|
1991
|
+
attributeNameOrAttributesNames[0]:attributeNameOrAttributesNames;
|
|
1986
1992
|
if(parentObject[attributeName]==null){
|
|
1987
1993
|
// NO : parentObject[attributeName]=defaultValue;
|
|
1988
1994
|
// BECAUSE CAUTION : if we have defaultValue as parameter, sometimes because of the recursive call, it can refer to a persisting object ! Thus, creating a strange bug.
|
|
1989
|
-
// (SO WE ALWAYS NEED TO START AS A BLANK STATE FOR DEFAULT VALUE
|
|
1990
|
-
const DEFAULT_VALUE={};
|
|
1995
|
+
// (SO WE ALWAYS NEED TO START AS A BLANK STATE FOR DEFAULT VALUE !)
|
|
1996
|
+
const DEFAULT_VALUE=(forceArrayAttributeDefaultValue?[]:{});
|
|
1991
1997
|
parentObject[attributeName]=DEFAULT_VALUE;
|
|
1992
1998
|
}
|
|
1993
1999
|
let foundOrCreatedChild=parentObject[attributeName];
|
|
1994
2000
|
return foundOrCreatedChild;
|
|
1995
2001
|
}
|
|
1996
|
-
if(isArray(
|
|
2002
|
+
if(isArray(attributeNameOrAttributesNames)){
|
|
1997
2003
|
|
|
1998
|
-
const attributeName=
|
|
2004
|
+
const attributeName=attributeNameOrAttributesNames[0];
|
|
1999
2005
|
if(parentObject[attributeName]==null){
|
|
2000
2006
|
// NO : parentObject[attributeName]=defaultValue;
|
|
2001
2007
|
// BECAUSE CAUTION : if we have defaultValue as parameter, sometimes because of the recursive call, it can refer to a persisting object ! Thus, creating a strange bug.
|
|
2002
|
-
// (SO WE ALWAYS NEED TO START AS A BLANK STATE FOR DEFAULT VALUE
|
|
2003
|
-
const DEFAULT_VALUE={};
|
|
2008
|
+
// (SO WE ALWAYS NEED TO START AS A BLANK STATE FOR DEFAULT VALUE !)
|
|
2009
|
+
const DEFAULT_VALUE=(forceArrayAttributeDefaultValue?[]:{});
|
|
2004
2010
|
parentObject[attributeName]=DEFAULT_VALUE;
|
|
2005
2011
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2012
|
+
// We remove the just-created child to allow for the other children to be created on the parent object :
|
|
2013
|
+
const slicedAttributesNames=attributeNameOrAttributesNames.slice(1);
|
|
2014
|
+
let foundOrCreatedChild=getOrCreateEmptyAttribute(parentObject, slicedAttributesNames, forceArrayAttributeDefaultValue);
|
|
2008
2015
|
return foundOrCreatedChild;
|
|
2009
2016
|
}
|
|
2010
|
-
return null; // (case no child found
|
|
2017
|
+
return null; // (case no child was found or it could be not created.)
|
|
2011
2018
|
}
|
|
2012
2019
|
|
|
2013
2020
|
window.nonull=function(value,defaultValue){
|
|
@@ -2209,6 +2216,14 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
|
|
|
2209
2216
|
return null;
|
|
2210
2217
|
}
|
|
2211
2218
|
|
|
2219
|
+
window.findInArray=function(collection, equalsFunction, returnFunction=null){
|
|
2220
|
+
return !!foreach(collection, item=>{
|
|
2221
|
+
if(returnFunction)
|
|
2222
|
+
return returnFunction(item);
|
|
2223
|
+
return item;
|
|
2224
|
+
}, equalsFunction);
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2212
2227
|
window.getKeyAt=function(associativeOrNormalArray,index){
|
|
2213
2228
|
return getAt(associativeOrNormalArray,index,true);
|
|
2214
2229
|
}
|
|
@@ -2263,7 +2278,6 @@ window.getNow=function(){
|
|
|
2263
2278
|
}
|
|
2264
2279
|
|
|
2265
2280
|
|
|
2266
|
-
|
|
2267
2281
|
// UNUSED:
|
|
2268
2282
|
// CAUTION : this is a costly method...
|
|
2269
2283
|
window.gotoValueLinearly=function(source, destination, doOnRefreshTreatment,/*OPTIONAL*/onEndMethod,/*OPTIONAL*/refreshingRateMillisParam,/*OPTIONAL*/
|
|
@@ -2823,7 +2837,6 @@ window.uncapitalize=function(str){
|
|
|
2823
2837
|
window.copy=function(array){
|
|
2824
2838
|
var result=null;
|
|
2825
2839
|
|
|
2826
|
-
// if(!isAssociative){
|
|
2827
2840
|
if(array instanceof Array){
|
|
2828
2841
|
|
|
2829
2842
|
result=new Array();
|
|
@@ -2831,7 +2844,6 @@ window.copy=function(array){
|
|
|
2831
2844
|
result.push(array[i]);
|
|
2832
2845
|
} else {
|
|
2833
2846
|
// Case of regular objects that are like associative arrays :
|
|
2834
|
-
|
|
2835
2847
|
result=new Object();
|
|
2836
2848
|
for (key in array){
|
|
2837
2849
|
if(!array.hasOwnProperty(key))
|
|
@@ -2946,10 +2958,15 @@ Math.productInArray=function(arrayOfValues){
|
|
|
2946
2958
|
return product;
|
|
2947
2959
|
};
|
|
2948
2960
|
|
|
2961
|
+
Math.average=function(value1,value2){
|
|
2962
|
+
return (value1+value2)*.5;
|
|
2963
|
+
};
|
|
2964
|
+
|
|
2965
|
+
|
|
2949
2966
|
Math.averageInArray=function(arrayOfValues){
|
|
2950
2967
|
if(empty(arrayOfValues))
|
|
2951
2968
|
return null;
|
|
2952
|
-
return Math.sumInArray(arrayOfValues)
|
|
2969
|
+
return Math.sumInArray(arrayOfValues)/arrayOfValues.length;
|
|
2953
2970
|
};
|
|
2954
2971
|
|
|
2955
2972
|
Math.maxInArray=function(arrayOfValues, returnKey=false){
|
|
@@ -3832,8 +3849,15 @@ window.isFunction=function(functionToCheck){
|
|
|
3832
3849
|
window.isPrimitive=function(p){
|
|
3833
3850
|
return (isNumber(p) || isString(p) || isBoolean(p));
|
|
3834
3851
|
}
|
|
3835
|
-
window.isObject=function(
|
|
3836
|
-
return !isFunction(
|
|
3852
|
+
window.isObject=function(o){ // (includes also plain objects)
|
|
3853
|
+
return !isFunction(o) && !isPrimitive(o);
|
|
3854
|
+
}
|
|
3855
|
+
// NO : DOES NOT WORK !!!
|
|
3856
|
+
//window.isPlainObject=function(o){
|
|
3857
|
+
// return Object.prototype.toString.call(o)==="[object Object]";
|
|
3858
|
+
//}
|
|
3859
|
+
window.isClassObject=function(o){
|
|
3860
|
+
return isObject(o) && !isNativeClass(o);
|
|
3837
3861
|
}
|
|
3838
3862
|
window.getClassName=function(obj){
|
|
3839
3863
|
if(!obj) return null;
|
|
@@ -4165,8 +4189,12 @@ JSON.parseRecycle=function(str){
|
|
|
4165
4189
|
|
|
4166
4190
|
|
|
4167
4191
|
window.clone=function(obj){
|
|
4192
|
+
// OLD :
|
|
4168
4193
|
const str=JSON.stringifyDecycle(obj);
|
|
4169
4194
|
const newObj=JSON.parseRecycle(str);
|
|
4195
|
+
// DOES NOT WORK :
|
|
4196
|
+
//const newObj=deepClone(obj);
|
|
4197
|
+
//const newObj=structuredClone(obj);
|
|
4170
4198
|
return newObj;
|
|
4171
4199
|
};
|
|
4172
4200
|
|
|
@@ -4696,9 +4724,9 @@ window.getAsTreeStructure=function(oldMap, keepClassName=false
|
|
|
4696
4724
|
};
|
|
4697
4725
|
|
|
4698
4726
|
|
|
4699
|
-
|
|
4727
|
+
window.restoreGraph=(flatData, keepClassName=false, UUID_ATTR_NAME, CLASSNAME_ATTR_NAME, POINTER_TO_ATTR_NAME)=>{
|
|
4700
4728
|
|
|
4701
|
-
|
|
4729
|
+
const revive=(id, keepClassName, restoredObjects = {})=>{
|
|
4702
4730
|
|
|
4703
4731
|
if(!id || typeof(flatData[id])==="undefined" || flatData[id]==null) return null;
|
|
4704
4732
|
const found=restoredObjects[id];
|
|
@@ -5044,7 +5072,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
|
|
|
5044
5072
|
|
|
5045
5073
|
|
|
5046
5074
|
|
|
5047
|
-
/*utils CLIENT library associated with aotra version : «1_29072022-2359 (
|
|
5075
|
+
/*utils CLIENT library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
|
|
5048
5076
|
/*-----------------------------------------------------------------------------*/
|
|
5049
5077
|
/* ## Utility global methods in a browser (htmljs) client environment.
|
|
5050
5078
|
*
|
|
@@ -5064,8 +5092,6 @@ AOTRAUTILS_LIB_IS_LOADED=true;
|
|
|
5064
5092
|
*/
|
|
5065
5093
|
|
|
5066
5094
|
|
|
5067
|
-
// Node dependencies :
|
|
5068
|
-
if(typeof(require)!="undefined" && typeof(sjcl)=="undefined" ) sjcl = require("sjcl");
|
|
5069
5095
|
|
|
5070
5096
|
|
|
5071
5097
|
// COMPATIBILITY browser javascript / nodejs environment :
|
|
@@ -6730,6 +6756,91 @@ function htmlColorCodeToDecimalArray(hexColorCodeParam){
|
|
|
6730
6756
|
return [ hex2dec(hexColorCode.substring(0, 2)), hex2dec(hexColorCode.substring(2, 4)), hex2dec(hexColorCode.substring(4, 6)) ];
|
|
6731
6757
|
}
|
|
6732
6758
|
|
|
6759
|
+
window.ascii2RGBA=(rgbaWholeString, pixelIndex)=>{
|
|
6760
|
+
const asciiValueRed = rgbaWholeString.charCodeAt(pixelIndex);
|
|
6761
|
+
const r=asciiValueRed;
|
|
6762
|
+
const asciiValueGreen = rgbaWholeString.charCodeAt(pixelIndex+1);
|
|
6763
|
+
const g=asciiValueGreen;
|
|
6764
|
+
const asciiValueBlue = rgbaWholeString.charCodeAt(pixelIndex+2);
|
|
6765
|
+
const b=asciiValueBlue;
|
|
6766
|
+
const asciiValueAlpha = rgbaWholeString.charCodeAt(pixelIndex+3);
|
|
6767
|
+
const a=asciiValueAlpha;
|
|
6768
|
+
return {r:r, g:g, b:b, a:a};
|
|
6769
|
+
}
|
|
6770
|
+
|
|
6771
|
+
window.ascii2RGB=(rgbWholeString, pixelIndex=0)=>{
|
|
6772
|
+
const asciiValueRed = rgbWholeString.charCodeAt(pixelIndex);
|
|
6773
|
+
const r=asciiValueRed;
|
|
6774
|
+
const asciiValueGreen = rgbWholeString.charCodeAt(pixelIndex+1);
|
|
6775
|
+
const g=asciiValueGreen;
|
|
6776
|
+
const asciiValueBlue = rgbWholeString.charCodeAt(pixelIndex+2);
|
|
6777
|
+
const b=asciiValueBlue;
|
|
6778
|
+
return {r:r, g:g, b:b};
|
|
6779
|
+
}
|
|
6780
|
+
|
|
6781
|
+
// UNUSED
|
|
6782
|
+
window.asciiStringToNumber=(str)=>{
|
|
6783
|
+
if(!str || empty(str)){
|
|
6784
|
+
throw new Error("ERROR : String must not be empty. Aborting string conversion to number.");
|
|
6785
|
+
}
|
|
6786
|
+
|
|
6787
|
+
let result=0;
|
|
6788
|
+
let shift=0;
|
|
6789
|
+
for(let i=0;i<str.length;i++){
|
|
6790
|
+
// Get the ASCII (UTF-16 code unit) for each character
|
|
6791
|
+
const c = str.charCodeAt(i);
|
|
6792
|
+
if(255<c){
|
|
6793
|
+
throw new Error("Character «"+c+"» is outside the ASCII range (0-255). Aborting.");
|
|
6794
|
+
}
|
|
6795
|
+
// Combine them into a single number using bitwise shifts
|
|
6796
|
+
// char1 is shifted 16 bits to the left, char2 8 bits, and char3 remains in the lowest 8 bits.
|
|
6797
|
+
result=((c << shift) | result);
|
|
6798
|
+
shift+=8;
|
|
6799
|
+
}
|
|
6800
|
+
|
|
6801
|
+
return result;
|
|
6802
|
+
}
|
|
6803
|
+
|
|
6804
|
+
window.convertBase255NumberRepresentedInUTF16tringToBase10Number=(str)=>{
|
|
6805
|
+
const BASE = 255;
|
|
6806
|
+
|
|
6807
|
+
if(!str || empty(str))
|
|
6808
|
+
return 0;
|
|
6809
|
+
|
|
6810
|
+
let result=0;
|
|
6811
|
+
// We reverse the string digits order :
|
|
6812
|
+
for(let i=str.length-1;0<=i;i--){
|
|
6813
|
+
let charCode=str.charCodeAt(i);
|
|
6814
|
+
// To handle the «modifier letter apostrophe» (UTF8 code 700)) sent as a replacement
|
|
6815
|
+
// for the «single quote» character (ASCII base table code 39) that is invalid in JSON text :
|
|
6816
|
+
if(charCode==700) charCode=39;
|
|
6817
|
+
result+=Math.pow(BASE, str.length-1-i)*charCode;
|
|
6818
|
+
// // DBG
|
|
6819
|
+
// lognow("int:" + str.charCodeAt(i) + ",char:" + str[i]+"|");
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6822
|
+
return result;
|
|
6823
|
+
};
|
|
6824
|
+
|
|
6825
|
+
|
|
6826
|
+
// UNUSED
|
|
6827
|
+
window.numberToAsciiString=(num, originalStringSize)=>{
|
|
6828
|
+
if(!originalStringSize)
|
|
6829
|
+
return "";
|
|
6830
|
+
|
|
6831
|
+
let result="";
|
|
6832
|
+
let shift=(originalStringSize-1)*8;
|
|
6833
|
+
for(let i=0;i<originalStringSize;i++){
|
|
6834
|
+
// Extract characters using bitwise shifts and masking
|
|
6835
|
+
result+=String.fromCharCode((num >> shift) & 0xFF);
|
|
6836
|
+
}
|
|
6837
|
+
|
|
6838
|
+
return result;
|
|
6839
|
+
}
|
|
6840
|
+
|
|
6841
|
+
|
|
6842
|
+
|
|
6843
|
+
|
|
6733
6844
|
|
|
6734
6845
|
/*private*/function componentToHex(c){
|
|
6735
6846
|
var hex=c.toString(16);
|
|
@@ -7987,8 +8098,12 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
7987
8098
|
|
|
7988
8099
|
if(!isUserMediaAvailable()){
|
|
7989
8100
|
// TRACE
|
|
7990
|
-
log("ERROR : User medias is not supported in your browser
|
|
8101
|
+
log("ERROR : User medias is not supported in your browser, or you are running in a non-https context, or you are running in a nodejs context. Aborting");
|
|
7991
8102
|
return Promise.resolve();
|
|
8103
|
+
}else{
|
|
8104
|
+
// TODO : FIXME :
|
|
8105
|
+
// Use https://www.npmjs.com/package/node-webcam (based on fswebcam) or https://github.com/yocontra/camera (based on opencv4)
|
|
8106
|
+
// to handle all the «nodejs context running»-like cases
|
|
7992
8107
|
}
|
|
7993
8108
|
|
|
7994
8109
|
return new Promise((accept,reject)=>{
|
|
@@ -8960,8 +9075,7 @@ function loadSoundsLoop(filesPaths,doOnLoaded=null,doOnEnded=null){
|
|
|
8960
9075
|
let audios=[];
|
|
8961
9076
|
let numberLoaded=0;
|
|
8962
9077
|
foreach(filesPaths,(filePath)=>{
|
|
8963
|
-
|
|
8964
|
-
|
|
9078
|
+
const sound=loadSound2D(filePath,function(selfParam/*UNUSED*/){
|
|
8965
9079
|
numberLoaded++
|
|
8966
9080
|
if(doOnLoaded && numberLoaded==filesPaths.length){
|
|
8967
9081
|
|
|
@@ -8971,10 +9085,9 @@ function loadSoundsLoop(filesPaths,doOnLoaded=null,doOnEnded=null){
|
|
|
8971
9085
|
// To prevent starting treatments after the loading has occurred after the loop has been stopped !
|
|
8972
9086
|
// (rare case when the loading is so long that the user has aborted the loop before all loop files has been loaded !)
|
|
8973
9087
|
if(!selfSoundsLoop.hasAbortedLooping) doOnLoaded(selfSoundsLoop);
|
|
8974
|
-
|
|
8975
9088
|
}
|
|
8976
|
-
|
|
8977
|
-
|
|
9089
|
+
});
|
|
9090
|
+
audios.push(sound);
|
|
8978
9091
|
});
|
|
8979
9092
|
|
|
8980
9093
|
return selfSoundsLoop;
|
|
@@ -9190,7 +9303,7 @@ class Sound{
|
|
|
9190
9303
|
}
|
|
9191
9304
|
|
|
9192
9305
|
|
|
9193
|
-
function
|
|
9306
|
+
function loadSound2D(filePath,doOnLoaded=null,doOnEnded=null){
|
|
9194
9307
|
|
|
9195
9308
|
let self=new Sound(filePath,doOnEnded);
|
|
9196
9309
|
|
|
@@ -11663,19 +11776,19 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
11663
11776
|
// DBG
|
|
11664
11777
|
lognow("INFO : Setting up client :...");
|
|
11665
11778
|
|
|
11666
|
-
let
|
|
11667
|
-
if(!
|
|
11779
|
+
let socketToServerClientInstance=WebsocketImplementation.getStatic(isNodeContext, useSocketIOImplementation).connectToServer(url, port, isSecure, timeout);
|
|
11780
|
+
if(!socketToServerClientInstance){
|
|
11668
11781
|
// TRACE
|
|
11669
|
-
lognow("ERROR : CLIENT : Could not get
|
|
11782
|
+
lognow("ERROR : CLIENT : Could not get socketToServerClientInstance, aborting client connection.");
|
|
11670
11783
|
return null;
|
|
11671
11784
|
}
|
|
11672
11785
|
|
|
11673
11786
|
// When client is connected, we execute the callback :
|
|
11674
11787
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
11675
|
-
|
|
11788
|
+
socketToServerClientInstance.onConnectionToServer(()=>{
|
|
11676
11789
|
if(doOnServerConnection){
|
|
11677
|
-
if(selfParam) doOnServerConnection.apply(selfParam,[
|
|
11678
|
-
else doOnServerConnection(
|
|
11790
|
+
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance]);
|
|
11791
|
+
else doOnServerConnection(socketToServerClientInstance);
|
|
11679
11792
|
}
|
|
11680
11793
|
|
|
11681
11794
|
|
|
@@ -11696,18 +11809,18 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
11696
11809
|
// else doOnError();
|
|
11697
11810
|
// };
|
|
11698
11811
|
//
|
|
11699
|
-
//
|
|
11812
|
+
// socketToServerClientInstance.clientSocket.on("connect_error", errorMethod);
|
|
11700
11813
|
//
|
|
11701
11814
|
//// // DOES NOT SEEM TO WORK :
|
|
11702
|
-
////
|
|
11815
|
+
//// socketToServerClientInstance.on("connect_failed", errorMethod);
|
|
11703
11816
|
//// // DOES NOT SEEM TO WORK :
|
|
11704
|
-
////
|
|
11817
|
+
//// socketToServerClientInstance.on("error", errorMethod);
|
|
11705
11818
|
//
|
|
11706
11819
|
// }
|
|
11707
11820
|
//
|
|
11708
11821
|
// // Data messages handling :
|
|
11709
11822
|
// if(doOnMessage){
|
|
11710
|
-
//
|
|
11823
|
+
// socketToServerClientInstance.onIncomingMessage((message)=>{
|
|
11711
11824
|
// if(selfParam) doOnMessage.apply(selfParam,[message]);
|
|
11712
11825
|
// else doOnMessage(message);
|
|
11713
11826
|
// });
|
|
@@ -11723,7 +11836,7 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
11723
11836
|
// });
|
|
11724
11837
|
|
|
11725
11838
|
|
|
11726
|
-
aotraClient.client.
|
|
11839
|
+
aotraClient.client.socketToServerClientInstance=socketToServerClientInstance;
|
|
11727
11840
|
|
|
11728
11841
|
return aotraClient;
|
|
11729
11842
|
};
|
|
@@ -11739,20 +11852,18 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
11739
11852
|
|
|
11740
11853
|
class VNCFrame2D{
|
|
11741
11854
|
|
|
11742
|
-
constructor(url, port=
|
|
11855
|
+
constructor(url, port=4000, htmlConfig={parentElement:null,imageMode:"CSSBackground"}, mouseEventsRefreshMillis=100){
|
|
11743
11856
|
|
|
11744
11857
|
|
|
11745
11858
|
this.url=url;
|
|
11746
11859
|
this.port=port;
|
|
11747
|
-
this.isSecure=
|
|
11860
|
+
this.isSecure=(contains(url.toLowerCase(),"wss"));
|
|
11748
11861
|
this.parentElement=nonull(htmlConfig.parentElement, document.body);
|
|
11749
11862
|
this.imageMode=htmlConfig.imageMode;
|
|
11750
11863
|
this.mouseEventsRefreshMillis=mouseEventsRefreshMillis;
|
|
11751
11864
|
|
|
11752
|
-
|
|
11753
11865
|
this.fusrodaClient=null;
|
|
11754
11866
|
|
|
11755
|
-
|
|
11756
11867
|
this.screenAndAudioConfig=null;
|
|
11757
11868
|
this.image=null;
|
|
11758
11869
|
self.audioCtx=null;
|
|
@@ -11784,7 +11895,6 @@ class VNCFrame2D{
|
|
|
11784
11895
|
// lognow("Image finished loading.");
|
|
11785
11896
|
|
|
11786
11897
|
const image=event.target;
|
|
11787
|
-
image.isImageLoading=false;
|
|
11788
11898
|
|
|
11789
11899
|
if(!self.canvasSizeHasBeenSet){
|
|
11790
11900
|
self.canvas.width=image.width;
|
|
@@ -11793,25 +11903,25 @@ class VNCFrame2D{
|
|
|
11793
11903
|
}
|
|
11794
11904
|
|
|
11795
11905
|
self.canvas.getContext("2d").drawImage(image,0,0);
|
|
11906
|
+
image.isImageLoading=false;
|
|
11796
11907
|
|
|
11797
|
-
self.fusrodaClient.client.
|
|
11908
|
+
self.fusrodaClient.client.socketToServerClientInstance.send("screenFrameRequest", {});
|
|
11798
11909
|
|
|
11799
11910
|
});
|
|
11800
11911
|
|
|
11801
11912
|
}
|
|
11802
11913
|
|
|
11803
|
-
|
|
11804
|
-
|
|
11914
|
+
if(self.screenAndAudioConfig.sampleRate)
|
|
11915
|
+
self.audioCtx=new AudioContext({sampleRate: self.screenAndAudioConfig.sampleRate});
|
|
11805
11916
|
|
|
11806
11917
|
|
|
11807
11918
|
},// doOnDataReception:
|
|
11808
11919
|
{
|
|
11809
|
-
"
|
|
11810
|
-
|
|
11811
|
-
|
|
11920
|
+
/*DEPRECATED, NOT HANDLED ANYMORE*/"image64":(imageDataStr)=>{
|
|
11812
11921
|
const base64String="data:image/png;base64,"+imageDataStr;
|
|
11813
11922
|
if(self.imageMode=="canvas"){
|
|
11814
11923
|
if(self.image.complete){
|
|
11924
|
+
if(self.image.isImageLoading) return;
|
|
11815
11925
|
self.image.src=base64String;
|
|
11816
11926
|
self.image.isImageLoading=true;
|
|
11817
11927
|
}
|
|
@@ -11819,12 +11929,11 @@ class VNCFrame2D{
|
|
|
11819
11929
|
self.parentElement.style["background-image"]="url('"+base64String+"');";
|
|
11820
11930
|
}
|
|
11821
11931
|
|
|
11822
|
-
|
|
11823
11932
|
},
|
|
11824
11933
|
"sound":(soundDataStr)=>{
|
|
11825
11934
|
const soundData=getDecodedArrayFromSoundDataString(soundDataStr);
|
|
11826
11935
|
playAudioData(soundData,self.audioCtx,self.screenAndAudioConfig.audioBufferSize,1,self.screenAndAudioConfig.sampleRate,()=>{
|
|
11827
|
-
self.fusrodaClient.client.
|
|
11936
|
+
self.fusrodaClient.client.socketToServerClientInstance.send("soundSampleRequest", {});
|
|
11828
11937
|
});
|
|
11829
11938
|
}
|
|
11830
11939
|
}, this.url, this.port, this.isSecure);
|
|
@@ -11937,7 +12046,27 @@ class VNCFrame2D{
|
|
|
11937
12046
|
}
|
|
11938
12047
|
|
|
11939
12048
|
|
|
11940
|
-
|
|
12049
|
+
window.base64ToUint8ClampedArray=(base64String)=>{
|
|
12050
|
+
const binaryString=window.atob(base64String);
|
|
12051
|
+
|
|
12052
|
+
const pixelsNumber=(binaryString.length);
|
|
12053
|
+
let bytes=new Uint8ClampedArray(pixelsNumber*4);
|
|
12054
|
+
for (let i=0; i<pixelsNumber; i++) {
|
|
12055
|
+
|
|
12056
|
+
let p=binaryString.charCodeAt(i);
|
|
12057
|
+
let a=(p>>24) & 0xff;
|
|
12058
|
+
let r=(p>>16) & 0xff;
|
|
12059
|
+
let g=(p>>8) & 0xff;
|
|
12060
|
+
let b=(p) & 0xff;
|
|
12061
|
+
|
|
12062
|
+
bytes[i]=r;
|
|
12063
|
+
bytes[i+1]=g;
|
|
12064
|
+
bytes[i+2]=b;
|
|
12065
|
+
bytes[i+3]=a;
|
|
12066
|
+
}
|
|
12067
|
+
|
|
12068
|
+
return bytes;
|
|
12069
|
+
};
|
|
11941
12070
|
|
|
11942
12071
|
|
|
11943
12072
|
// ============================================== BROWSER CLIENTS ==============================================
|
|
@@ -11945,67 +12074,66 @@ class VNCFrame2D{
|
|
|
11945
12074
|
|
|
11946
12075
|
// FUSRODA :
|
|
11947
12076
|
|
|
11948
|
-
createFusrodaClient=function(doOnClientReady, doOnDataReception, urlParam=null, portParam=
|
|
12077
|
+
createFusrodaClient=function(doOnClientReady, doOnDataReception, urlParam=null, portParam=4000){
|
|
11949
12078
|
// CAUTION : WORKS BETTER WHEN UNSECURE, BUT 'NEEDS CLIENT BROWSER TO ALLOW MIXED (SECURE/UNSECURE) CONTENT !
|
|
11950
12079
|
|
|
11951
|
-
const isSecure=(contains(urlParam.toLowerCase(),"https") || contains(urlParam.toLowerCase(),"wss"));
|
|
11952
12080
|
|
|
12081
|
+
const isSecure=(contains(urlParam.toLowerCase(),"https") || contains(urlParam.toLowerCase(),"wss"));
|
|
11953
12082
|
|
|
11954
|
-
const fusrodaClient=initClient(false
|
|
12083
|
+
const fusrodaClient=initClient(false,
|
|
12084
|
+
/*CAUTION : Fusroda Java server requires the Socket IO websocket implementation !!*/true,
|
|
12085
|
+
/*doOnServerConnection:*/(socketToServerClientInstance)=>{
|
|
11955
12086
|
|
|
11956
12087
|
// DBG
|
|
11957
12088
|
lognow("FUSRODA CLIENT : SETTING UP...");
|
|
11958
12089
|
|
|
11959
|
-
|
|
11960
12090
|
//0)
|
|
11961
|
-
|
|
12091
|
+
socketToServerClientInstance.receive("protocolConfig", (messageConfig)=>{
|
|
11962
12092
|
|
|
11963
12093
|
// DBG
|
|
11964
12094
|
lognow("(CLIENT) (DEBUG) Received protocol config from server : messageConfig:",messageConfig);
|
|
11965
12095
|
|
|
11966
|
-
//2)
|
|
11967
|
-
socketToServer.receive("imagePacket", (doOnDataReception && doOnDataReception["image"]?doOnDataReception["image"]:()=>{/*DO NOTHING*/}));
|
|
11968
|
-
|
|
11969
|
-
// //DBG
|
|
11970
|
-
// lognow("Initial image request.");
|
|
11971
|
-
|
|
11972
12096
|
// Initial frame request sending :
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
//2)
|
|
11976
|
-
socketToServer.receive("soundPacket", (doOnDataReception && doOnDataReception["sound"]?doOnDataReception["sound"]:()=>{/*DO NOTHING*/}));
|
|
12097
|
+
socketToServerClientInstance.send("screenFrameRequest", {});
|
|
11977
12098
|
// Initial sound sample request sending :
|
|
11978
|
-
|
|
11979
|
-
|
|
12099
|
+
socketToServerClientInstance.send("soundSampleRequest", {});
|
|
11980
12100
|
|
|
11981
12101
|
doOnClientReady(messageConfig);
|
|
11982
12102
|
});
|
|
12103
|
+
|
|
12104
|
+
//2)
|
|
12105
|
+
/*DEPRECATED, NOT HANDLED ANYMORE*/socketToServerClientInstance.receive("image64Packet", (doOnDataReception && doOnDataReception["image64"]?doOnDataReception["image64"]:()=>{/*DO NOTHING*/}));
|
|
12106
|
+
socketToServerClientInstance.receive("imagePacket", (doOnDataReception && doOnDataReception["image"]?doOnDataReception["image"]:()=>{/*DO NOTHING*/}));
|
|
12107
|
+
socketToServerClientInstance.receive("imageDiffPacket", (doOnDataReception && doOnDataReception["imageDiff"]?doOnDataReception["imageDiff"]:()=>{/*DO NOTHING*/}));
|
|
12108
|
+
socketToServerClientInstance.receive("soundPacket", (doOnDataReception && doOnDataReception["sound"]?doOnDataReception["sound"]:()=>{/*DO NOTHING*/}));
|
|
12109
|
+
|
|
12110
|
+
// DBG
|
|
12111
|
+
lognow("(CLIENT) SENDING «hello» TO SERVER...");
|
|
11983
12112
|
|
|
11984
12113
|
//1)
|
|
11985
|
-
|
|
12114
|
+
socketToServerClientInstance.send("protocol", { type:"hello" });
|
|
11986
12115
|
|
|
11987
12116
|
|
|
11988
12117
|
}, urlParam, portParam, isSecure);
|
|
11989
12118
|
|
|
11990
12119
|
fusrodaClient.sendMouseMoveEvent=(mouseEvent)=>{
|
|
11991
|
-
fusrodaClient.client.
|
|
12120
|
+
fusrodaClient.client.socketToServerClientInstance.send("mouseMoveEvent", mouseEvent);
|
|
11992
12121
|
};
|
|
11993
|
-
|
|
11994
12122
|
fusrodaClient.sendMouseClickEvent=(mouseEvent)=>{
|
|
11995
|
-
fusrodaClient.client.
|
|
12123
|
+
fusrodaClient.client.socketToServerClientInstance.send("mouseClickEvent", mouseEvent);
|
|
11996
12124
|
};
|
|
11997
12125
|
fusrodaClient.sendMouseReleasedEvent=(mouseEvent)=>{
|
|
11998
|
-
fusrodaClient.client.
|
|
12126
|
+
fusrodaClient.client.socketToServerClientInstance.send("mouseReleasedEvent", mouseEvent);
|
|
11999
12127
|
};
|
|
12000
12128
|
fusrodaClient.sendWheelEvent=(wheelEvent)=>{
|
|
12001
|
-
fusrodaClient.client.
|
|
12129
|
+
fusrodaClient.client.socketToServerClientInstance.send("wheelEvent", wheelEvent);
|
|
12002
12130
|
};
|
|
12003
12131
|
|
|
12004
12132
|
fusrodaClient.sendKeyboardPressedEvent=(keyboardEvent)=>{
|
|
12005
|
-
fusrodaClient.client.
|
|
12133
|
+
fusrodaClient.client.socketToServerClientInstance.send("keyboardPressedEvent", keyboardEvent);
|
|
12006
12134
|
};
|
|
12007
12135
|
fusrodaClient.sendKeyboardReleasedEvent=(keyboardEvent)=>{
|
|
12008
|
-
fusrodaClient.client.
|
|
12136
|
+
fusrodaClient.client.socketToServerClientInstance.send("keyboardReleasedEvent", keyboardEvent);
|
|
12009
12137
|
};
|
|
12010
12138
|
|
|
12011
12139
|
|
|
@@ -12189,7 +12317,7 @@ window.createOritaMainClient=function(
|
|
|
12189
12317
|
|
|
12190
12318
|
|
|
12191
12319
|
|
|
12192
|
-
const oritaClient=initClient(false, false, /*doOnServerConnection*/(
|
|
12320
|
+
const oritaClient=initClient(false, false, /*doOnServerConnection*/(socketToServerClientInstance)=>{
|
|
12193
12321
|
|
|
12194
12322
|
// DBG
|
|
12195
12323
|
lognow("ORITA MAIN CLIENT : SETTING UP");
|
|
@@ -12216,7 +12344,7 @@ window.createOritaMainClient=function(
|
|
|
12216
12344
|
storeString(ORITA_HASH_STRING_NAME, calculatedHash);
|
|
12217
12345
|
|
|
12218
12346
|
// TODO : FIXME : DUPLICATED CODE :
|
|
12219
|
-
|
|
12347
|
+
socketToServerClientInstance.send("protocol", "request.register:mainClient:"+calculatedHash);
|
|
12220
12348
|
|
|
12221
12349
|
},()=>{
|
|
12222
12350
|
// TRACE
|
|
@@ -12225,7 +12353,7 @@ window.createOritaMainClient=function(
|
|
|
12225
12353
|
|
|
12226
12354
|
}else{
|
|
12227
12355
|
// TODO : FIXME : DUPLICATED CODE :
|
|
12228
|
-
|
|
12356
|
+
socketToServerClientInstance.send("protocol", "request.register:mainClient:"+storedHashKey);
|
|
12229
12357
|
}
|
|
12230
12358
|
|
|
12231
12359
|
|
|
@@ -12242,7 +12370,7 @@ window.createOritaMainClient=function(
|
|
|
12242
12370
|
|
|
12243
12371
|
|
|
12244
12372
|
|
|
12245
|
-
oritaClient.client.
|
|
12373
|
+
oritaClient.client.socketToServerClientInstance.receive("protocol", (message)=>{
|
|
12246
12374
|
|
|
12247
12375
|
// TRACE
|
|
12248
12376
|
lognow("Mainclient received message from server:",message);
|
|
@@ -12260,7 +12388,7 @@ window.createOritaMainClient=function(
|
|
|
12260
12388
|
|
|
12261
12389
|
|
|
12262
12390
|
// Main client starts receiving :
|
|
12263
|
-
oritaClient.client.
|
|
12391
|
+
oritaClient.client.socketToServerClientInstance.receive("server.send.data", (receivedData)=>{
|
|
12264
12392
|
|
|
12265
12393
|
|
|
12266
12394
|
let microClientId=receivedData.microClientId;
|
|
@@ -12343,7 +12471,7 @@ window.createOritaMainClient=function(
|
|
|
12343
12471
|
|
|
12344
12472
|
|
|
12345
12473
|
|
|
12346
|
-
oritaClient.client.
|
|
12474
|
+
oritaClient.client.socketToServerClientInstance.receive("communication", (message)=>{
|
|
12347
12475
|
|
|
12348
12476
|
// DBG
|
|
12349
12477
|
lognow("MAIN CLIENT RECEIVED COMMUNICATION :",message);
|
|
@@ -12581,7 +12709,7 @@ const HALF_STEP_STEPPER_SEQUENCE = [
|
|
|
12581
12709
|
|
|
12582
12710
|
|
|
12583
12711
|
|
|
12584
|
-
createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdParam=null){
|
|
12712
|
+
createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdParam=null, videoDataHook=null){
|
|
12585
12713
|
|
|
12586
12714
|
|
|
12587
12715
|
let audioBufferSize = ORITA_CONSTANTS.AUDIO_BUFFER_SIZE;
|
|
@@ -12593,6 +12721,8 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12593
12721
|
const oritaClient={
|
|
12594
12722
|
|
|
12595
12723
|
staticMicroClientId:staticMicroClientId,
|
|
12724
|
+
videoDataHook:videoDataHook,
|
|
12725
|
+
// audioDataHook:audioDataHook,// TODO !
|
|
12596
12726
|
|
|
12597
12727
|
onRegistrationEventListeners:[],
|
|
12598
12728
|
onCommunicationEventListeners:[],
|
|
@@ -12663,10 +12793,8 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12663
12793
|
// TODO : FIXME : Utiliser initClient(...) au lieu de directement getStatic(...) avec le paramètre isNode transmis dans l'appel)
|
|
12664
12794
|
//oritaClient=initClient(isNode,false,doOnServerConnection=null, url, port);
|
|
12665
12795
|
oritaClient.client={};
|
|
12666
|
-
oritaClient.client.
|
|
12667
|
-
oritaClient.client.
|
|
12668
|
-
|
|
12669
|
-
|
|
12796
|
+
oritaClient.client.socketToServerClientInstance=WebsocketImplementation.getStatic(isNode).connectToServer(url, port);
|
|
12797
|
+
oritaClient.client.socketToServerClientInstance.onConnectionToServer(() => {
|
|
12670
12798
|
|
|
12671
12799
|
|
|
12672
12800
|
// TRACE
|
|
@@ -12682,17 +12810,13 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12682
12810
|
lognow("INFO : (CLIENT) Microclient will now register to server with microClientId «" + microClientId + "».");
|
|
12683
12811
|
|
|
12684
12812
|
|
|
12685
|
-
oritaClient.client.
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12813
|
+
oritaClient.client.socketToServerClientInstance.send("protocol", "request.register:microClient:" + microClientId);
|
|
12689
12814
|
|
|
12690
12815
|
|
|
12691
12816
|
|
|
12692
|
-
|
|
12693
12817
|
// ------------------------------ CONNECTION EVENTS REGISTRATION ------------------------------
|
|
12694
12818
|
|
|
12695
|
-
oritaClient.client.
|
|
12819
|
+
oritaClient.client.socketToServerClientInstance.receive("protocol", (message) => {
|
|
12696
12820
|
|
|
12697
12821
|
// TRACE
|
|
12698
12822
|
lognow("INFO : MICRO CLIENT : Server sent a message on the protocol channel : message:", stringifyObject(message));
|
|
@@ -12741,41 +12865,54 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12741
12865
|
// console.log("INFO : MICRO CLIENT : videoData : ",videoData);
|
|
12742
12866
|
|
|
12743
12867
|
if (videoData && videoData.data) {
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12868
|
+
|
|
12869
|
+
if(oritaClient.videoDataHook && oritaClient.videoDataHook.doVideoAnalysis){
|
|
12870
|
+
data.videoAnalysis={
|
|
12871
|
+
treatmentName:oritaClient.videoDataHook.doVideoAnalysis.treatmentName,
|
|
12872
|
+
treatmentResult:(oritaClient.videoDataHook.doVideoAnalysis.doTreatment?
|
|
12873
|
+
oritaClient.videoDataHook.doVideoAnalysis.doTreatment(videoData)
|
|
12874
|
+
:null),
|
|
12875
|
+
};
|
|
12876
|
+
}
|
|
12877
|
+
|
|
12878
|
+
|
|
12879
|
+
if(!oritaClient.videoDataHook || (oritaClient.videoDataHook && oritaClient.videoDataHook.doNotSendToServer!=true)){
|
|
12880
|
+
|
|
12881
|
+
let videoRawData = videoData.data;
|
|
12882
|
+
|
|
12883
|
+
if (videoData.format !== "base64" && videoData.messWithAlpha) {
|
|
12884
|
+
videoRawData = removeAlpha(videoRawData);
|
|
12885
|
+
}
|
|
12886
|
+
|
|
12887
|
+
// // TRACE
|
|
12888
|
+
// lognow("CLIENT : getting to send : videoRawData.length:"+videoRawData.length);
|
|
12889
|
+
|
|
12890
|
+
// Actually, only handles "LZW" compression, for now...
|
|
12891
|
+
|
|
12892
|
+
let stringToSend;
|
|
12893
|
+
if (ORITA_CONSTANTS.STRINGIFY_VIDEO_DATA) stringToSend = arrayToString(videoRawData, false, 1);
|
|
12894
|
+
else stringToSend = videoRawData;
|
|
12895
|
+
|
|
12896
|
+
// We set the Side is «left» or «right»
|
|
12897
|
+
if (videoSide) data.videoSide = videoSide;
|
|
12898
|
+
|
|
12899
|
+
|
|
12900
|
+
data.video = {
|
|
12901
|
+
width: videoData.width,
|
|
12902
|
+
height: videoData.height,
|
|
12903
|
+
data: stringToSend,
|
|
12904
|
+
messWithAlpha: videoData.messWithAlpha,
|
|
12905
|
+
format: videoData.format,
|
|
12906
|
+
};
|
|
12907
|
+
data.compression = {
|
|
12908
|
+
precision: 1,
|
|
12909
|
+
//algorithm:"LZW"
|
|
12910
|
+
};
|
|
12911
|
+
|
|
12750
12912
|
}
|
|
12751
|
-
|
|
12752
|
-
// We set the Side is «left» or «right»
|
|
12753
|
-
if (videoSide) data.videoSide = videoSide;
|
|
12754
|
-
|
|
12755
|
-
// // TRACE
|
|
12756
|
-
// lognow("CLIENT : getting to send : videoRawData.length:"+videoRawData.length);
|
|
12757
|
-
|
|
12758
|
-
// Actually, only handles "LZW" compression, for now...
|
|
12759
|
-
|
|
12760
|
-
let stringToSend;
|
|
12761
|
-
if (ORITA_CONSTANTS.STRINGIFY_VIDEO_DATA) stringToSend = arrayToString(videoRawData, false, 1);
|
|
12762
|
-
else stringToSend = videoRawData;
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
data.video = {
|
|
12767
|
-
width: videoData.width,
|
|
12768
|
-
height: videoData.height,
|
|
12769
|
-
data: stringToSend,
|
|
12770
|
-
messWithAlpha: videoData.messWithAlpha,
|
|
12771
|
-
format: videoData.format,
|
|
12772
|
-
};
|
|
12773
|
-
data.compression = {
|
|
12774
|
-
precision: 1,
|
|
12775
|
-
//algorithm:"LZW"
|
|
12776
|
-
};
|
|
12777
12913
|
|
|
12778
12914
|
}
|
|
12915
|
+
|
|
12779
12916
|
|
|
12780
12917
|
} else if (medias === "audio") {
|
|
12781
12918
|
|
|
@@ -12803,6 +12940,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12803
12940
|
return;
|
|
12804
12941
|
}
|
|
12805
12942
|
|
|
12943
|
+
|
|
12806
12944
|
|
|
12807
12945
|
// // TRACE
|
|
12808
12946
|
// lognow("CLIENT : SENDING DATA TO SERVER :");
|
|
@@ -12813,12 +12951,11 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12813
12951
|
data.microClientId = oritaClient.microClientId;
|
|
12814
12952
|
|
|
12815
12953
|
// Micro client starts sending :
|
|
12816
|
-
oritaClient.client.
|
|
12954
|
+
oritaClient.client.socketToServerClientInstance.send("microClient.send.data", data);
|
|
12817
12955
|
|
|
12818
12956
|
|
|
12819
12957
|
|
|
12820
|
-
}
|
|
12821
|
-
, refreshingRateMillis); // SENDING LOOP
|
|
12958
|
+
}, refreshingRateMillis); // SENDING LOOP
|
|
12822
12959
|
// *************************
|
|
12823
12960
|
}
|
|
12824
12961
|
|
|
@@ -12836,7 +12973,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12836
12973
|
|
|
12837
12974
|
});
|
|
12838
12975
|
|
|
12839
|
-
oritaClient.client.
|
|
12976
|
+
oritaClient.client.socketToServerClientInstance.receive("communication", (message) => {
|
|
12840
12977
|
// We execute eventual on message events listeners :
|
|
12841
12978
|
foreach(oritaClient.onCommunicationEventListeners, (e) => {
|
|
12842
12979
|
if (!e.condition || e.condition(message)) e.execute(message);
|
|
@@ -12902,7 +13039,6 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12902
13039
|
} else { // Case node micro client :
|
|
12903
13040
|
|
|
12904
13041
|
|
|
12905
|
-
|
|
12906
13042
|
// DBG
|
|
12907
13043
|
console.log("mediaParameters", mediaParameters);
|
|
12908
13044
|
console.log("medias", medias);
|
|
@@ -12992,7 +13128,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
12992
13128
|
// DBG
|
|
12993
13129
|
lognow("SIGNALING INPUTS");
|
|
12994
13130
|
|
|
12995
|
-
oritaClient.client.
|
|
13131
|
+
oritaClient.client.socketToServerClientInstance.send("communication", {
|
|
12996
13132
|
type: "request.microClient.appendInputs",
|
|
12997
13133
|
inputsGPIO: oritaClient.inputsGPIO,
|
|
12998
13134
|
microClientId: oritaClient.microClientId,
|
|
@@ -13004,7 +13140,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13004
13140
|
// DBG
|
|
13005
13141
|
lognow("SIGNALING OUTPUTS");
|
|
13006
13142
|
|
|
13007
|
-
oritaClient.client.
|
|
13143
|
+
oritaClient.client.socketToServerClientInstance.send("communication", {
|
|
13008
13144
|
type: "request.microClient.appendOutputs",
|
|
13009
13145
|
outputsGPIO: oritaClient.outputsGPIO,
|
|
13010
13146
|
microClientId: oritaClient.microClientId,
|
|
@@ -13055,7 +13191,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13055
13191
|
// 1-
|
|
13056
13192
|
oritaClient.onRegistrationEventListeners.push({
|
|
13057
13193
|
execute: (microClientId, message) => {
|
|
13058
|
-
oritaClient.client.
|
|
13194
|
+
oritaClient.client.socketToServerClientInstance.send("communication", {
|
|
13059
13195
|
type: "request.microClient.appendInputs",
|
|
13060
13196
|
inputsGPIO: oritaClient.inputsGPIO,
|
|
13061
13197
|
microClientId: oritaClient.microClientId,
|
|
@@ -13073,7 +13209,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13073
13209
|
});
|
|
13074
13210
|
|
|
13075
13211
|
|
|
13076
|
-
oritaClient.client.
|
|
13212
|
+
oritaClient.client.socketToServerClientInstance.send("communication", {
|
|
13077
13213
|
type: "request.microClient.updateInputsValues",
|
|
13078
13214
|
inputsGPIO: inputsGPIO,
|
|
13079
13215
|
microClientId: oritaClient.microClientId,
|
|
@@ -13260,7 +13396,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13260
13396
|
// 1-
|
|
13261
13397
|
oritaClient.onRegistrationEventListeners.push({
|
|
13262
13398
|
execute: (microClientId, message) => {
|
|
13263
|
-
oritaClient.client.
|
|
13399
|
+
oritaClient.client.socketToServerClientInstance.send("communication", {
|
|
13264
13400
|
type: "request.microClient.appendOutputs",
|
|
13265
13401
|
outputsGPIO: oritaClient.outputsGPIO,
|
|
13266
13402
|
microClientId: oritaClient.microClientId,
|
|
@@ -13354,7 +13490,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13354
13490
|
// });
|
|
13355
13491
|
|
|
13356
13492
|
// 4-
|
|
13357
|
-
oritaClient.client.
|
|
13493
|
+
oritaClient.client.socketToServerClientInstance.send("communication", {
|
|
13358
13494
|
type: "response.microClient.outputChanged",
|
|
13359
13495
|
outputsGPIO: oritaClient.outputsGPIO,
|
|
13360
13496
|
});
|
|
@@ -13390,7 +13526,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13390
13526
|
oritaClient.sendCommand = (commandName, commandParameters = null) => {
|
|
13391
13527
|
const microClientId = oritaClient.microClientId;
|
|
13392
13528
|
const command = { microClientId: microClientId, type: "microClient.send.command", name: commandName, parameters: commandParameters };
|
|
13393
|
-
oritaClient.client.
|
|
13529
|
+
oritaClient.client.socketToServerClientInstance.send("communication", command);
|
|
13394
13530
|
return oritaClient;
|
|
13395
13531
|
};
|
|
13396
13532
|
|
|
@@ -13401,253 +13537,10 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13401
13537
|
|
|
13402
13538
|
|
|
13403
13539
|
|
|
13404
|
-
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
class AORTACClient{
|
|
13410
|
-
|
|
13411
|
-
constructor(clientId, serverNodeOrigin, model, view, isNodeContext=true, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
|
|
13412
|
-
this.clientId=clientId;
|
|
13413
|
-
this.serverNodeOrigin=serverNodeOrigin;
|
|
13414
|
-
this.isNodeContext=isNodeContext;
|
|
13415
|
-
this.sslConfig=sslConfig;
|
|
13416
|
-
|
|
13417
|
-
this.model=model; // must implement functions
|
|
13418
|
-
this.view=view; // Must implement functions
|
|
13419
|
-
this.view.setClient(this);
|
|
13420
|
-
|
|
13421
|
-
this.clientInstanceToReferenceServerNode=null;
|
|
13422
|
-
this.currentServersNodes={};
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
// TRACE
|
|
13426
|
-
lognow(`AORTAC client created with id ${this.clientId}.`);
|
|
13427
|
-
|
|
13428
|
-
}
|
|
13429
|
-
|
|
13430
|
-
start(){
|
|
13431
|
-
|
|
13432
|
-
const self=this;
|
|
13433
|
-
|
|
13434
|
-
const splits=splitURL(this.serverNodeOrigin);
|
|
13435
|
-
let protocol=nonull(splits.protocol,"ws"), host=nonull(splits.host,"localhost"), port=nonull(splits.port,"30000");
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
// THIS IS ON THE REFERENCE SERER NODE ONLY :
|
|
13439
|
-
const clientInstanceToReferenceServerNode=initClient(this.isNodeContext, false, (socketToServer)=>{
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
// First client needs to register to its main reference serer node :
|
|
13443
|
-
// TRACE
|
|
13444
|
-
lognow(` (client ${self.clientId}) Sending client registering request to server node...`);
|
|
13445
|
-
|
|
13446
|
-
const helloClientRequest={
|
|
13447
|
-
clientId:self.clientId,
|
|
13448
|
-
type:"request.register.client",
|
|
13449
|
-
isReferenceNode:true,
|
|
13450
|
-
};
|
|
13451
|
-
socketToServer.send("protocol", helloClientRequest);
|
|
13452
|
-
|
|
13453
|
-
// We place a listener from server on this client instance :
|
|
13454
|
-
socketToServer.receive("protocol",(message)=>{
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
// Adding listeners :
|
|
13458
|
-
if(message.type==="response.register.client"){
|
|
13459
|
-
|
|
13460
|
-
// TRACE
|
|
13461
|
-
lognow(` (${self.clientId}) Receving registering response from reference server...`);
|
|
13462
|
-
|
|
13463
|
-
// Case reference node:
|
|
13464
|
-
|
|
13465
|
-
// Second, once client is registered at its refernce server, then it needs to know to which servers it needs to connect to :
|
|
13466
|
-
// TRACE
|
|
13467
|
-
lognow("(client "+self.clientId+") Sending client interrogation request to server node...");
|
|
13468
|
-
|
|
13469
|
-
const boundaries=self.view.getBoundaries();
|
|
13470
|
-
const interrogationRequest={
|
|
13471
|
-
clientId:self.clientId,
|
|
13472
|
-
type:"request.interrogation.client",
|
|
13473
|
-
boundaries:boundaries
|
|
13474
|
-
};
|
|
13475
|
-
socketToServer.send("protocol", interrogationRequest);
|
|
13476
|
-
|
|
13477
|
-
|
|
13478
|
-
}
|
|
13479
|
-
});
|
|
13480
|
-
|
|
13481
|
-
// We place a listener from server on this client instance :
|
|
13482
|
-
socketToServer.receive("protocol",(message)=>{
|
|
13483
|
-
if(message.type==="response.unregister.client"){
|
|
13484
|
-
|
|
13485
|
-
// TRACE
|
|
13486
|
-
lognow(` (${self.clientId}) Receving unregistering response from reference server...`);
|
|
13487
|
-
|
|
13488
|
-
// DO NOTHING
|
|
13489
|
-
|
|
13490
|
-
}
|
|
13491
|
-
});
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
// We place a listener from server on this client instance :
|
|
13495
|
-
socketToServer.receive("protocol",(message)=>{
|
|
13496
|
-
if(message.type==="response.interrogation.client"){
|
|
13497
|
-
|
|
13498
|
-
// TRACE
|
|
13499
|
-
lognow("!!!!!! ("+self.clientId+") Receving interrogation response from requested server...",message);
|
|
13500
|
-
|
|
13501
|
-
// Now the client needs to know to which other nodes it needs to connect in order to get all its model objects :
|
|
13502
|
-
const serversNodesIdsForModelObjectsForClient=message.serversNodesIdsForModelObjectsForClient;
|
|
13503
|
-
|
|
13504
|
-
// Now the client needs to know to which other nodes it needs to connect in order to get all its model objects :
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
// We connect (only) to the relevant servers directly :
|
|
13508
|
-
foreach(serversNodesIdsForModelObjectsForClient,(nodeInfoAndObjectsIds, serverNodeId)=>{
|
|
13509
|
-
|
|
13510
|
-
const splitsServerInfo=splitURL(nodeInfoAndObjectsIds.nodeServerInfo);
|
|
13511
|
-
const clientInstanceToAuthorityServerNode=initClient(this.isNodeContext, false, (socketToServer)=>{
|
|
13512
|
-
|
|
13513
|
-
// Then we register to them :
|
|
13514
|
-
// TRACE
|
|
13515
|
-
lognow(` (client ${self.clientId}) Sending client registering request to authority server node...`);
|
|
13516
|
-
|
|
13517
|
-
const helloToAuthorityClientRequest={
|
|
13518
|
-
clientId:self.clientId,
|
|
13519
|
-
type:"request.register.client",
|
|
13520
|
-
isReferenceNode:false,
|
|
13521
|
-
};
|
|
13522
|
-
socketToServer.send("protocol", helloToAuthorityClientRequest);
|
|
13523
|
-
|
|
13524
|
-
|
|
13525
|
-
}, splitsServerInfo.protocol+"://"+splitsServerInfo.host, splitsServerInfo.port, false);
|
|
13526
|
-
self.currentServersNodes[serverNodeId]={clientInstance:clientInstanceToAuthorityServerNode};
|
|
13527
|
-
clientInstanceToAuthorityServerNode.client.start();
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
clientInstanceToAuthorityServerNode.client.socketToServer.receive("protocol", (message)=>{
|
|
13531
|
-
|
|
13532
|
-
// Adding listeners :
|
|
13533
|
-
if(message.type==="response.register.client"){
|
|
13534
|
-
// We place a listener from server on this client instance :
|
|
13535
|
-
// Adding listeners :
|
|
13536
|
-
|
|
13537
|
-
// Case non-reference authority node servers :
|
|
13538
|
-
|
|
13539
|
-
// TRACE
|
|
13540
|
-
lognow(` (${self.clientId}) Receving registering response from reference server...`);
|
|
13541
|
-
|
|
13542
|
-
// We merge our client's model with the partial model sent from the server it just registered to :
|
|
13543
|
-
const partialModelString=message.partialModelString;
|
|
13544
|
-
const partialModel=JSON.parseRecycle(partialModelString);
|
|
13545
|
-
|
|
13546
|
-
// Merging means that all objects in partialModel not being in model must be added to model :
|
|
13547
|
-
self.model.clientMergeWith(partialModel);
|
|
13548
|
-
|
|
13549
|
-
// TRACE
|
|
13550
|
-
lognow("(client "+self.clientId+") Client model has been merged with a partial model from a server node...",self.model);
|
|
13551
|
-
|
|
13552
|
-
}
|
|
13553
|
-
|
|
13554
|
-
|
|
13555
|
-
});
|
|
13556
|
-
|
|
13557
|
-
|
|
13558
|
-
|
|
13559
|
-
clientInstanceToAuthorityServerNode.client.socketToServer.receive("inputs", (message)=>{
|
|
13560
|
-
|
|
13561
|
-
// Adding listeners :
|
|
13562
|
-
if(message.type==="response.objectsModifiedOrAdded.client"){
|
|
13563
|
-
const modifiedOrAddedObjects=JSON.parseRecycle(message.objectsString);
|
|
13564
|
-
|
|
13565
|
-
if(!message.isAddingObjects){
|
|
13566
|
-
self.model.clientUpdateObjects(modifiedOrAddedObjects);
|
|
13567
|
-
}else{
|
|
13568
|
-
self.model.clientAddObjects(modifiedOrAddedObjects);
|
|
13569
|
-
}
|
|
13570
|
-
|
|
13571
|
-
// TRACE
|
|
13572
|
-
lognow(` (client ${self.clientId}) Receving modified or added objects after the inputs...modifiedOrAddedObjects=`,modifiedOrAddedObjects);
|
|
13573
|
-
|
|
13574
|
-
|
|
13575
|
-
}
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
});
|
|
13579
|
-
|
|
13580
|
-
|
|
13581
|
-
});
|
|
13582
|
-
|
|
13583
|
-
}
|
|
13584
|
-
});
|
|
13585
|
-
|
|
13586
|
-
|
|
13587
|
-
//self.addListeners();
|
|
13588
|
-
|
|
13589
|
-
|
|
13590
|
-
|
|
13591
|
-
}, protocol+"://"+host, port, false);
|
|
13592
|
-
clientInstanceToReferenceServerNode.client.start();
|
|
13593
|
-
this.clientInstanceToReferenceServerNode={clientInstance:clientInstanceToReferenceServerNode};
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
return this;
|
|
13597
|
-
}
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
/*public*/sendInputs(inputs, subBoundaries=null){
|
|
13601
|
-
|
|
13602
|
-
const self=this;
|
|
13603
|
-
|
|
13604
|
-
|
|
13605
|
-
if(subBoundaries){
|
|
13606
|
-
// We send client's inputs to ALL its currently connected servers :
|
|
13607
|
-
const self=this;
|
|
13608
|
-
foreach(this.currentServersNodes,(serversNode,serverNodeId)=>{
|
|
13609
|
-
self.sendInputsToServer(serversNode, inputs, subBoundaries);
|
|
13610
|
-
});
|
|
13611
|
-
}else{
|
|
13612
|
-
// If we have no boundaries set for these inputs, then we only send them to one server node at random :
|
|
13613
|
-
const serversNode=Math.getRandomInArray(this.currentServersNodes);
|
|
13614
|
-
this.sendInputsToServer(serversNode, inputs, subBoundaries);
|
|
13615
|
-
}
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
}
|
|
13619
|
-
|
|
13620
|
-
/*private*/sendInputsToServer(serversNode, inputs, subBoundaries=null){
|
|
13621
|
-
const clientInstance=serversNode.clientInstance;
|
|
13622
|
-
|
|
13623
|
-
const inputsMessage={
|
|
13624
|
-
clientId:this.clientId,
|
|
13625
|
-
type:"request.inputs.client",
|
|
13626
|
-
inputs:inputs,
|
|
13627
|
-
subBoundaries:subBoundaries,
|
|
13628
|
-
};
|
|
13629
|
-
|
|
13630
|
-
clientInstance.client.socketToServer.send("inputs", inputsMessage);
|
|
13631
|
-
}
|
|
13632
|
-
|
|
13633
|
-
|
|
13634
|
-
|
|
13635
|
-
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
}
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
getAORTACClient=function(clientId=getUUID(), serverNodeOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=true){
|
|
13642
|
-
return new AORTACClient(clientId, serverNodeOrigin, model, view, isNodeContext);
|
|
13643
|
-
}
|
|
13644
|
-
|
|
13645
|
-
|
|
13646
|
-
|
|
13647
13540
|
|
|
13648
13541
|
|
|
13649
13542
|
|
|
13650
|
-
/*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (
|
|
13543
|
+
/*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
|
|
13651
13544
|
/*-----------------------------------------------------------------------------*/
|
|
13652
13545
|
|
|
13653
13546
|
|
|
@@ -13792,6 +13685,30 @@ Math.rotateAround=function(angleRadians, xParam=0, yParam=0, xCenterParam=0, yCe
|
|
|
13792
13685
|
return point;
|
|
13793
13686
|
};
|
|
13794
13687
|
|
|
13688
|
+
Math.rotatePointAround3D=function(angleRadiansOnY, angleRadiansOnZ, pointToRotate, rotationCenterPoint={x:0,y:0,z:0}){
|
|
13689
|
+
const xy=Math.rotateAround(angleRadiansOnY, pointToRotate.x, pointToRotate.y, rotationCenterPoint.x, rotationCenterPoint.y);
|
|
13690
|
+
const xz=Math.rotateAround(angleRadiansOnZ, pointToRotate.x, pointToRotate.z, rotationCenterPoint.x, rotationCenterPoint.z);
|
|
13691
|
+
return {x: xy.x, y:xy.y, z:xz.y};
|
|
13692
|
+
}
|
|
13693
|
+
|
|
13694
|
+
Math.addCoordinates=function(points, functionToApplyAtEachSum=null){
|
|
13695
|
+
if(!points || empty(points)) return null;
|
|
13696
|
+
const resultPoint={};
|
|
13697
|
+
foreach(points, (point)=>{
|
|
13698
|
+
foreach(point, (coordinateComponent,coordinateComponentName)=>{
|
|
13699
|
+
const resultPointCoordinateComponent=resultPoint[coordinateComponentName];
|
|
13700
|
+
if(typeof(resultPointCoordinateComponent)=="undefined" || resultPointCoordinateComponent==null)
|
|
13701
|
+
resultPoint[coordinateComponentName]=0;
|
|
13702
|
+
if(functionToApplyAtEachSum)
|
|
13703
|
+
resultPoint[coordinateComponentName]=functionToApplyAtEachSum(resultPoint[coordinateComponentName]+coordinateComponent);
|
|
13704
|
+
else
|
|
13705
|
+
resultPoint[coordinateComponentName]+=coordinateComponent;
|
|
13706
|
+
});
|
|
13707
|
+
});
|
|
13708
|
+
return resultPoint;
|
|
13709
|
+
}
|
|
13710
|
+
|
|
13711
|
+
|
|
13795
13712
|
// UNUSED (AND UNUSEFUL) :
|
|
13796
13713
|
//Math.getGluedPosition=function(referencePosition, relativePosition){
|
|
13797
13714
|
// const referenceAngleRadians=referencePosition.getAngle2D();
|
|
@@ -14026,7 +13943,7 @@ window.calculateLinearlyMovedPoint3DPolar=function(currentPoint, anglesRadians,
|
|
|
14026
13943
|
//test();
|
|
14027
13944
|
|
|
14028
13945
|
|
|
14029
|
-
Math.coerceAngle=function(angle,isRadians=false,isOnlyPositive=false){
|
|
13946
|
+
Math.coerceAngle=function(angle, isRadians=false, isOnlyPositive=false){
|
|
14030
13947
|
const WHOLE_ARC=(isRadians?Math.TAU:360);
|
|
14031
13948
|
let result=0;
|
|
14032
13949
|
if(angle <= 0) result=(WHOLE_ARC - Math.abs(angle % WHOLE_ARC)) % WHOLE_ARC;
|
|
@@ -14119,73 +14036,8 @@ function verticesToLinePoints(polygon, isLineKeptFunction=null){
|
|
|
14119
14036
|
}
|
|
14120
14037
|
return lines;
|
|
14121
14038
|
}
|
|
14122
|
-
// helper: test line intersections
|
|
14123
|
-
// point object: {x:, y:}
|
|
14124
|
-
// p0 & p1 form one segment, p2 & p3 form the second segment
|
|
14125
|
-
// Get interseting point of 2 line segments (if any)
|
|
14126
|
-
// Attribution: http://paulbourke.net/geometry/pointlineplane/
|
|
14127
|
-
function lineSegmentsCollide(segment1Point1,segment1Point2,segment2Point1,segment2Point2) {
|
|
14128
|
-
|
|
14129
|
-
// ORIGINAL :
|
|
14130
|
-
// var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x);
|
|
14131
|
-
// var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x);
|
|
14132
|
-
// var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
let deltaXPoint1=segment1Point1.x-segment2Point1.x;
|
|
14136
|
-
let deltaYPoint1=segment1Point1.y-segment2Point1.y;
|
|
14137
|
-
let deltaYPoint2=segment2Point2.y-segment2Point1.y;
|
|
14138
|
-
|
|
14139
|
-
let deltaXSegment1Point2_1=segment1Point2.x-segment1Point1.x;
|
|
14140
|
-
let deltaYSegment1Point2_1=segment1Point2.y-segment1Point1.y;
|
|
14141
|
-
let deltaXSegment2Point2_1=segment2Point2.x-segment2Point1.x;
|
|
14142
|
-
|
|
14143
|
-
let numerator1 = (deltaXSegment2Point2_1 * deltaYPoint1) - (deltaYPoint2 * deltaXPoint1);
|
|
14144
|
-
let numerator2 = (deltaXSegment1Point2_1 * deltaYPoint1) - (deltaYSegment1Point2_1 * deltaXPoint1);
|
|
14145
|
-
let denominator = (deltaYPoint2 * deltaXSegment1Point2_1) - (deltaXSegment2Point2_1 * deltaYSegment1Point2_1);
|
|
14146
|
-
|
|
14147
|
-
// Test if Coincident
|
|
14148
|
-
// If the denominator and numerator for the ua and ub are 0
|
|
14149
|
-
// then the two lines are coincident.
|
|
14150
|
-
if(numerator1==0 && numerator2==0 && denominator==0) return false;
|
|
14151
|
-
|
|
14152
|
-
// Test if Parallel
|
|
14153
|
-
// If the denominator for the equations for ua and ub is 0
|
|
14154
|
-
// then the two lines are parallel.
|
|
14155
|
-
if(denominator == 0) return false;
|
|
14156
|
-
|
|
14157
|
-
// test if line segments are colliding
|
|
14158
|
-
numerator1 = numerator1/denominator;
|
|
14159
|
-
numerator2 = numerator2/denominator;
|
|
14160
|
-
return (numerator1>=0 && numerator1<=1 && numerator2>=0 && numerator2<=1);
|
|
14161
|
-
}
|
|
14162
|
-
|
|
14163
|
-
|
|
14164
14039
|
|
|
14165
|
-
// http://alienryderflex.com/polygon/
|
|
14166
|
-
// 1) We draw a line parallel to the X axis, at the Y coordinate of the tested point,
|
|
14167
|
-
// 2) And we collect all the intersection points (or nodes) with the polygon's sides.
|
|
14168
|
-
// (We exclude nodes that have the same coordinates as polygon vertices !)
|
|
14169
|
-
// 3) If there is an odd number (or 1) of nodes on each side (on the X axis) of the point, then the point is inside the polygon
|
|
14170
|
-
|
|
14171
|
-
function getSegmentEquationParameters(p1,p2){
|
|
14172
|
-
const deltaX=p2.x-p1.x;
|
|
14173
|
-
const deltaY=p2.y-p1.y;
|
|
14174
|
-
if(deltaX==0) return null;
|
|
14175
|
-
if(deltaY==0) return {slope:0, yOffset:p2.y};
|
|
14176
|
-
const slope=(deltaX/deltaY);
|
|
14177
|
-
const yOffset=-(slope*p1.x-p1.y);
|
|
14178
|
-
return {slope:slope, yOffset:yOffset};
|
|
14179
|
-
}
|
|
14180
14040
|
|
|
14181
|
-
function getIntersectionPointOnSegmentAtY(p1,p2,yTarget){
|
|
14182
|
-
if((yTarget<p1.y && yTarget<p2.y) || (p1.y<yTarget && p2.y<yTarget)) return null;
|
|
14183
|
-
const equationParameters=getSegmentEquationParameters(p1,p2);
|
|
14184
|
-
if(!equationParameters) return null;
|
|
14185
|
-
if(equationParameters.slope==0) return null;
|
|
14186
|
-
const xCollision=(yTarget-equationParameters.yOffset)/equationParameters.slope;
|
|
14187
|
-
return {x:xCollision, y:yTarget};
|
|
14188
|
-
}
|
|
14189
14041
|
|
|
14190
14042
|
Math.isPointInPolygon=function(point,polygon){
|
|
14191
14043
|
|
|
@@ -14271,6 +14123,137 @@ window.isOriented=function(orientation, width, height){
|
|
|
14271
14123
|
|
|
14272
14124
|
|
|
14273
14125
|
|
|
14126
|
+
// Lines
|
|
14127
|
+
|
|
14128
|
+
// helper: test line intersections
|
|
14129
|
+
// point object: {x:, y:}
|
|
14130
|
+
// p0 & p1 form one segment, p2 & p3 form the second segment
|
|
14131
|
+
// Get interseting point of 2 line segments (if any)
|
|
14132
|
+
// Attribution: http://paulbourke.net/geometry/pointlineplane/
|
|
14133
|
+
function lineSegmentsCollide(segment1Point1,segment1Point2,segment2Point1,segment2Point2) {
|
|
14134
|
+
|
|
14135
|
+
// ORIGINAL :
|
|
14136
|
+
// var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x);
|
|
14137
|
+
// var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x);
|
|
14138
|
+
// var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);
|
|
14139
|
+
|
|
14140
|
+
|
|
14141
|
+
let deltaXPoint1=segment1Point1.x-segment2Point1.x;
|
|
14142
|
+
let deltaYPoint1=segment1Point1.y-segment2Point1.y;
|
|
14143
|
+
let deltaYPoint2=segment2Point2.y-segment2Point1.y;
|
|
14144
|
+
|
|
14145
|
+
let deltaXSegment1Point2_1=segment1Point2.x-segment1Point1.x;
|
|
14146
|
+
let deltaYSegment1Point2_1=segment1Point2.y-segment1Point1.y;
|
|
14147
|
+
let deltaXSegment2Point2_1=segment2Point2.x-segment2Point1.x;
|
|
14148
|
+
|
|
14149
|
+
let numerator1 = (deltaXSegment2Point2_1 * deltaYPoint1) - (deltaYPoint2 * deltaXPoint1);
|
|
14150
|
+
let numerator2 = (deltaXSegment1Point2_1 * deltaYPoint1) - (deltaYSegment1Point2_1 * deltaXPoint1);
|
|
14151
|
+
let denominator = (deltaYPoint2 * deltaXSegment1Point2_1) - (deltaXSegment2Point2_1 * deltaYSegment1Point2_1);
|
|
14152
|
+
|
|
14153
|
+
// Test if Coincident
|
|
14154
|
+
// If the denominator and numerator for the ua and ub are 0
|
|
14155
|
+
// then the two lines are coincident.
|
|
14156
|
+
if(numerator1==0 && numerator2==0 && denominator==0) return false;
|
|
14157
|
+
|
|
14158
|
+
// Test if Parallel
|
|
14159
|
+
// If the denominator for the equations for ua and ub is 0
|
|
14160
|
+
// then the two lines are parallel.
|
|
14161
|
+
if(denominator == 0) return false;
|
|
14162
|
+
|
|
14163
|
+
// test if line segments are colliding
|
|
14164
|
+
numerator1 = numerator1/denominator;
|
|
14165
|
+
numerator2 = numerator2/denominator;
|
|
14166
|
+
return (numerator1>=0 && numerator1<=1 && numerator2>=0 && numerator2<=1);
|
|
14167
|
+
}
|
|
14168
|
+
|
|
14169
|
+
|
|
14170
|
+
window.getIntersectionPointWithYHorizontalLineAndLineEquationParameters=function(slope,yOffset,yLineHeight){
|
|
14171
|
+
if(slope==0) return null;
|
|
14172
|
+
return {x:(yLineHeight-yOffset)/slope,y:yLineHeight};
|
|
14173
|
+
}
|
|
14174
|
+
|
|
14175
|
+
window.getIntersectionPointFrom2LinesEquationParameters=function(slope1,yOffset1,slope2,yOffset2){
|
|
14176
|
+
const slopesDelta=slope1-slope2;
|
|
14177
|
+
if(slopesDelta==0) return null;
|
|
14178
|
+
|
|
14179
|
+
const yOffsetsInverseDelta=yOffset2-yOffset1;
|
|
14180
|
+
|
|
14181
|
+
const intersectionX=yOffsetsInverseDelta/slopesDelta;
|
|
14182
|
+
const intersectionY=(slope1*yOffset2-slope2*yOffset1)/slopesDelta;
|
|
14183
|
+
|
|
14184
|
+
return {x:intersectionX,y:intersectionY};
|
|
14185
|
+
}
|
|
14186
|
+
|
|
14187
|
+
window.getSegmentEquationParameters=function(p1,p2){
|
|
14188
|
+
const deltaX=p2.x-p1.x;
|
|
14189
|
+
const deltaY=p2.y-p1.y;
|
|
14190
|
+
if(deltaX==0) return null;
|
|
14191
|
+
if(deltaY==0) return {slope:0, yOffset:p2.y};
|
|
14192
|
+
const slope=(deltaX/deltaY);
|
|
14193
|
+
const yOffset=-(slope*p1.x-p1.y);
|
|
14194
|
+
return {slope:slope, yOffset:yOffset};
|
|
14195
|
+
}
|
|
14196
|
+
|
|
14197
|
+
// http://alienryderflex.com/polygon/
|
|
14198
|
+
// 1) We draw a line parallel to the X axis, at the Y coordinate of the tested point,
|
|
14199
|
+
// 2) And we collect all the intersection points (or nodes) with the polygon's sides.
|
|
14200
|
+
// (We exclude nodes that have the same coordinates as polygon vertices !)
|
|
14201
|
+
// 3) If there is an odd number (or 1) of nodes on each side (on the X axis) of the point, then the point is inside the polygon
|
|
14202
|
+
|
|
14203
|
+
window.getIntersectionPointOnSegmentAtY=function(p1,p2,yTarget){
|
|
14204
|
+
if((yTarget<p1.y && yTarget<p2.y) || (p1.y<yTarget && p2.y<yTarget)) return null;
|
|
14205
|
+
const equationParameters=getSegmentEquationParameters(p1,p2);
|
|
14206
|
+
if(!equationParameters) return null;
|
|
14207
|
+
if(equationParameters.slope==0) return null;
|
|
14208
|
+
const xCollision=(yTarget-equationParameters.yOffset)/equationParameters.slope;
|
|
14209
|
+
return {x:xCollision, y:yTarget};
|
|
14210
|
+
}
|
|
14211
|
+
|
|
14212
|
+
|
|
14213
|
+
|
|
14214
|
+
// Perspective
|
|
14215
|
+
|
|
14216
|
+
|
|
14217
|
+
// apparentCorner1----apparentCorner2
|
|
14218
|
+
// | |
|
|
14219
|
+
// | |
|
|
14220
|
+
// | |
|
|
14221
|
+
// | |
|
|
14222
|
+
// apparentCorner4----apparentCorner3
|
|
14223
|
+
window.getUntransformed2DCoordinatesInsidePerspectivedRectangle=function(apparentPoint,apparentCorner1,apparentCorner2,apparentCorner3,apparentCorner4){
|
|
14224
|
+
|
|
14225
|
+
const minX=Math.minInArray([apparentCorner1.x,apparentCorner2.x,apparentCorner3.x,apparentCorner4.x]);
|
|
14226
|
+
const minY=Math.minInArray([apparentCorner1.y,apparentCorner2.y,apparentCorner3.y,apparentCorner4.y]);
|
|
14227
|
+
const maxX=Math.maxInArray([apparentCorner1.x,apparentCorner2.x,apparentCorner3.x,apparentCorner4.x]);
|
|
14228
|
+
const maxY=Math.maxInArray([apparentCorner1.y,apparentCorner2.y,apparentCorner3.y,apparentCorner4.y]);
|
|
14229
|
+
|
|
14230
|
+
const apparentSize={w:(maxX-minX),h:(maxY-minY)};
|
|
14231
|
+
if(apparentSize.w==0) return null;
|
|
14232
|
+
|
|
14233
|
+
const heightRatio=(apparentSize.w<apparentSize.h ? 1 : (apparentSize.h/apparentSize.w));
|
|
14234
|
+
|
|
14235
|
+
const leftSideLineEquationParameters=getSegmentEquationParameters(apparentCorner1,apparentCorner4);
|
|
14236
|
+
if(!leftSideLineEquationParameters) return null;
|
|
14237
|
+
const rightSideLineEquationParameters=getSegmentEquationParameters(apparentCorner2,apparentCorner3);
|
|
14238
|
+
if(!rightSideLineEquationParameters) return null;
|
|
14239
|
+
|
|
14240
|
+
const perspectivePoint=getIntersectionPointFrom2LinesEquationParameters(
|
|
14241
|
+
leftSideLineEquationParameters.slope,leftSideLineEquationParameters.yOffset,
|
|
14242
|
+
rightSideLineEquationParameters.slope,rightSideLineEquationParameters.yOffset);
|
|
14243
|
+
if(!perspectivePoint) return null;
|
|
14244
|
+
|
|
14245
|
+
const perspetiveLine=getSegmentEquationParameters(apparentPoint,perspectivePoint);
|
|
14246
|
+
if(!perspetiveLine) return null;
|
|
14247
|
+
|
|
14248
|
+
// (for the X coordinate only)
|
|
14249
|
+
const untransformedPoint=getIntersectionPointWithYHorizontalLineAndLineEquationParameters(perspetiveLine.slope, perspetiveLine.yOffset, minY);
|
|
14250
|
+
const x=untransformedPoint.x;
|
|
14251
|
+
// (for the Y coordinate only)
|
|
14252
|
+
const y=(apparentPoint.y*heightRatio);
|
|
14253
|
+
|
|
14254
|
+
const result={x:x-minX, y:y-minY};
|
|
14255
|
+
return result;
|
|
14256
|
+
}
|
|
14274
14257
|
|
|
14275
14258
|
|
|
14276
14259
|
|
|
@@ -14886,10 +14869,10 @@ function rayVsUnitSphereClosestPoint(p, r) {
|
|
|
14886
14869
|
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
14887
14870
|
|
|
14888
14871
|
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
|
|
14889
|
-
/*utils 3D library associated with aotra version : «1_29072022-2359 (
|
|
14872
|
+
/*utils 3D library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
|
|
14890
14873
|
/*-----------------------------------------------------------------------------*/
|
|
14891
14874
|
|
|
14892
|
-
/*utils AI library associated with aotra version : «1_29072022-2359 (
|
|
14875
|
+
/*utils AI library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
|
|
14893
14876
|
/*-----------------------------------------------------------------------------*/
|
|
14894
14877
|
|
|
14895
14878
|
|
|
@@ -15035,11 +15018,11 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
15035
15018
|
|
|
15036
15019
|
|
|
15037
15020
|
|
|
15038
|
-
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (
|
|
15021
|
+
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (01/03/2026-01:46:57)»*/
|
|
15039
15022
|
/*-----------------------------------------------------------------------------*/
|
|
15040
15023
|
|
|
15041
15024
|
|
|
15042
|
-
/* ## Utility
|
|
15025
|
+
/* ## Utility methods in a javascript, for AORTAC subsystem (server & client)
|
|
15043
15026
|
*
|
|
15044
15027
|
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
15045
15028
|
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
@@ -15062,78 +15045,750 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
15062
15045
|
if(typeof(window)==="undefined") window=global;
|
|
15063
15046
|
|
|
15064
15047
|
|
|
15048
|
+
// ==================================================================================================================
|
|
15065
15049
|
|
|
15066
15050
|
|
|
15067
|
-
//
|
|
15068
|
-
// https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
|
|
15069
|
-
// https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
|
|
15070
|
-
// https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
|
|
15071
|
-
// https://socket.io/docs/v4/client-socket-instance/
|
|
15051
|
+
// AORTAC SERVER
|
|
15072
15052
|
|
|
15073
|
-
// NEW : ws :
|
|
15074
|
-
// https://github.com/websockets/ws#installing
|
|
15075
|
-
// https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
|
|
15076
|
-
// ON BROWSER SIDE : Native Websockets :
|
|
15077
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
15078
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
|
|
15079
15053
|
|
|
15080
|
-
|
|
15081
|
-
//Socket=require("socket.io");
|
|
15082
|
-
//WebSocket=require("ws");
|
|
15054
|
+
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
15083
15055
|
|
|
15056
|
+
AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT=30000;
|
|
15084
15057
|
|
|
15058
|
+
// New implementation :
|
|
15059
|
+
AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS=2000;
|
|
15060
|
+
class AORTACServerCell{
|
|
15061
|
+
|
|
15062
|
+
constructor(quorumNumber=1, selfOrigin, outcomingCellsOrigins, model, controller, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
|
|
15085
15063
|
|
|
15064
|
+
this.model=model;
|
|
15065
|
+
this.controller=controller;
|
|
15066
|
+
|
|
15067
|
+
this.quorumNumber=quorumNumber;
|
|
15068
|
+
this.selfOrigin=selfOrigin;
|
|
15069
|
+
const splits=splitURL(this.selfOrigin);
|
|
15070
|
+
this.protocol=nonull(splits.protocol,"ws");
|
|
15071
|
+
this.host=nonull(splits.host,"localhost");
|
|
15072
|
+
this.port=nonull(splits.port,"30000");
|
|
15073
|
+
this.sslConfig=sslConfig;
|
|
15086
15074
|
|
|
15087
|
-
|
|
15088
|
-
|
|
15075
|
+
this.server=null;
|
|
15076
|
+
this.incomingCells={};
|
|
15077
|
+
|
|
15078
|
+
this.outcomingCellsOrigins=outcomingCellsOrigins;
|
|
15079
|
+
this.outcomingCells={};
|
|
15080
|
+
const self=this;
|
|
15081
|
+
foreach(this.outcomingCellsOrigins,outcomingCellOrigin=>{
|
|
15082
|
+
self.outcomingCells[outcomingCellOrigin]={connected:false,client:null};
|
|
15083
|
+
});
|
|
15084
|
+
this.outcomingNetworkProber=null;
|
|
15085
|
+
|
|
15086
|
+
this.onReceiveMessageListeners={
|
|
15087
|
+
"incoming":{"protocol":{}},
|
|
15088
|
+
"outcoming":{"protocol":{}},
|
|
15089
|
+
"both":{"protocol":{}},
|
|
15090
|
+
};
|
|
15089
15091
|
|
|
15090
|
-
|
|
15092
|
+
this.cellsOverview={};
|
|
15093
|
+
|
|
15094
|
+
this.startTime=null;
|
|
15095
|
+
|
|
15096
|
+
this.quorumIsReachedSequenceIsInitiated=false;
|
|
15097
|
+
// this.quorumCells=null; // (CAUTION : only the latest ready cell when quorum is reached has this attribute populated)
|
|
15091
15098
|
|
|
15092
|
-
|
|
15099
|
+
this.liveModelObjects={};
|
|
15093
15100
|
|
|
15101
|
+
}
|
|
15094
15102
|
|
|
15103
|
+
start(){
|
|
15095
15104
|
|
|
15105
|
+
this.startTime=getNow();
|
|
15106
|
+
this.server=this.launchServerForIncomingConnections();
|
|
15096
15107
|
|
|
15108
|
+
if(empty(this.outcomingCellsOrigins) || (getArraySize(this.outcomingCellsOrigins)==1 && contains(this.outcomingCellsOrigins, this.selfOrigin))){
|
|
15109
|
+
// TRACE
|
|
15110
|
+
lognow("ERROR : Server cell must have an outcoming link to at least one other server cell of the blob. Aborting outcoming network probing.");
|
|
15111
|
+
}else{
|
|
15112
|
+
this.outcomingNetworkProber=new PeriodicalExecuter(this.probeOutcomingNetwork, AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS, this);
|
|
15113
|
+
}
|
|
15097
15114
|
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
getPersister=()=>{ return null; };
|
|
15103
|
-
|
|
15104
|
-
}else{
|
|
15115
|
+
this.handleCommonListeners();
|
|
15116
|
+
|
|
15117
|
+
return this;
|
|
15118
|
+
}
|
|
15105
15119
|
|
|
15120
|
+
/*private*/launchServerForIncomingConnections(){
|
|
15121
|
+
const self=this;
|
|
15122
|
+
|
|
15123
|
+
const server=initNodeServerInfrastructureWrapper(
|
|
15124
|
+
// On each client connection :
|
|
15125
|
+
null,
|
|
15126
|
+
// On client finalization :
|
|
15127
|
+
function(server){
|
|
15128
|
+
|
|
15129
|
+
// On-receive message listeners handling ;
|
|
15130
|
+
foreach(Object.keys(self.onReceiveMessageListeners["incoming"]), channelName=>{
|
|
15131
|
+
server.receive(channelName, (message, clientSocket)=>{
|
|
15132
|
+
self.executeListeners("incoming",channelName, message, clientSocket);
|
|
15133
|
+
});
|
|
15134
|
+
});
|
|
15135
|
+
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{
|
|
15136
|
+
server.receive(channelName, (message, clientSocket)=>{
|
|
15137
|
+
self.executeListeners("both",channelName, message, clientSocket);
|
|
15138
|
+
});
|
|
15139
|
+
});
|
|
15140
|
+
|
|
15141
|
+
|
|
15142
|
+
// HANDLE HELLO SEQUENCE
|
|
15143
|
+
self.handleHelloSequence();
|
|
15144
|
+
|
|
15145
|
+
}, this.port, this.sslConfig.certPath, this.sslConfig.keyPath);
|
|
15146
|
+
server.serverManager.start();
|
|
15147
|
+
return server;
|
|
15148
|
+
}
|
|
15106
15149
|
|
|
15107
|
-
|
|
15150
|
+
/*private*/probeOutcomingNetwork(){
|
|
15151
|
+
const numberOfTotalServers=getArraySize(this.outcomingCells);
|
|
15152
|
+
let numberOfConnectedOutcomingServers=0;
|
|
15153
|
+
foreach(this.outcomingCells, outcomingCell=>{
|
|
15154
|
+
numberOfConnectedOutcomingServers++
|
|
15155
|
+
},outcomingCell=>outcomingCell.connected);
|
|
15108
15156
|
|
|
15109
|
-
|
|
15157
|
+
// TRACE
|
|
15158
|
+
lognow(`INFO : Number of connected outcoming servers : ${numberOfConnectedOutcomingServers}/${numberOfTotalServers}`);
|
|
15110
15159
|
|
|
15111
|
-
|
|
15112
|
-
|
|
15160
|
+
if(numberOfTotalServers<=numberOfConnectedOutcomingServers){
|
|
15161
|
+
this.outcomingNetworkProber.stop();
|
|
15113
15162
|
|
|
15114
|
-
//
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15163
|
+
// TRACE
|
|
15164
|
+
lognow(`INFO : All outcoming cells connected. Stopping outcoming network probing.`);
|
|
15165
|
+
|
|
15166
|
+
// INITIATE CELL IS READY SEQUENCE
|
|
15167
|
+
this.initiateCellIsReadySequence();
|
|
15168
|
+
|
|
15169
|
+
return;
|
|
15170
|
+
}
|
|
15171
|
+
|
|
15172
|
+
const self=this;
|
|
15173
|
+
|
|
15174
|
+
// We try to connect to all outcoming cells :
|
|
15175
|
+
foreach(this.outcomingCells, (outcomingCell, outcomingCellOrigin)=>{
|
|
15122
15176
|
|
|
15123
|
-
|
|
15177
|
+
const splits=splitURL(outcomingCellOrigin);
|
|
15178
|
+
const outcomingCellProtocol=nonull(splits.protocol,"ws");
|
|
15179
|
+
const outcomingCellHost=nonull(splits.host,"localhost");
|
|
15180
|
+
const outcomingCellPort=nonull(splits.port,"40000");
|
|
15181
|
+
|
|
15182
|
+
const clientInstance=initClient(true, false, (socketToServerClientInstance)=>{
|
|
15124
15183
|
|
|
15125
|
-
|
|
15126
|
-
|
|
15127
|
-
|
|
15128
|
-
|
|
15129
|
-
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15184
|
+
// On-receive message listeners handling ;
|
|
15185
|
+
foreach(Object.keys(self.onReceiveMessageListeners["outcoming"]), channelName=>{
|
|
15186
|
+
socketToServerClientInstance.receive(channelName, (message, clientSocket)=>{
|
|
15187
|
+
self.executeListeners("outcoming",channelName, message, clientSocket);
|
|
15188
|
+
});
|
|
15189
|
+
});
|
|
15190
|
+
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{
|
|
15191
|
+
socketToServerClientInstance.receive(channelName, (message, clientSocket)=>{
|
|
15192
|
+
self.executeListeners("both",channelName, message, clientSocket);
|
|
15193
|
+
});
|
|
15194
|
+
});
|
|
15195
|
+
|
|
15196
|
+
|
|
15197
|
+
// INITIATE HELLO SEQUENCE
|
|
15198
|
+
self.initiateHelloSequence(socketToServerClientInstance);
|
|
15199
|
+
|
|
15200
|
+
// We are connected:
|
|
15201
|
+
outcomingCell.connected=true;
|
|
15202
|
+
// Only used to identify the outcoming node, DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
15203
|
+
outcomingCell.clientSocket=clientInstance.client.clientSocket;
|
|
15204
|
+
// For this, use the socketToServerClientInstance attribute instead :
|
|
15205
|
+
outcomingCell.socketToServerClientInstance=socketToServerClientInstance;
|
|
15206
|
+
|
|
15207
|
+
}, outcomingCellProtocol+"://"+outcomingCellHost, outcomingCellPort, false);
|
|
15208
|
+
|
|
15209
|
+
clientInstance.client.start();
|
|
15210
|
+
|
|
15211
|
+
clientInstance.client.socketToServerClientInstance.onServerLostListeners.push({execute:(clientSocket)=>{
|
|
15212
|
+
const foundOutcomingNode=findInArray(self.outcomingCells,outcomingCell=>outcomingCell.clientSocket==clientSocket);
|
|
15213
|
+
if(foundOutcomingNode) foundOutcomingNode.connected=false;
|
|
15214
|
+
}});
|
|
15215
|
+
|
|
15216
|
+
|
|
15217
|
+
},outcomingCell=>!outcomingCell.connected);
|
|
15218
|
+
|
|
15219
|
+
}
|
|
15220
|
+
|
|
15221
|
+
|
|
15222
|
+
/*private*/handleCommonListeners(){
|
|
15223
|
+
|
|
15224
|
+
// HANDLE CELL IS READY SEQUENCE
|
|
15225
|
+
this.handleCellIsReadySequence();
|
|
15226
|
+
|
|
15227
|
+
// HANDLE PARTITION SEQUENCE
|
|
15228
|
+
this.handlePartitionSequence();
|
|
15229
|
+
|
|
15230
|
+
}
|
|
15231
|
+
|
|
15232
|
+
|
|
15233
|
+
/*private*/executeListeners(outletName, channelName, message, clientSocket){
|
|
15234
|
+
const onReceiveMessageListeners=this.onReceiveMessageListeners[outletName][channelName];
|
|
15235
|
+
if(!onReceiveMessageListeners) return;
|
|
15236
|
+
|
|
15237
|
+
// OUTCOMING NODES LISTENERS HOOK :
|
|
15238
|
+
// // TRACE
|
|
15239
|
+
// lognow("INFO : SERVER : Received a "+channelName+" message: ", message);
|
|
15240
|
+
|
|
15241
|
+
foreach(onReceiveMessageListeners, (listeners,listenerMessageType)=>{
|
|
15242
|
+
foreach(listeners, (listener)=>{
|
|
15243
|
+
// // TRACE
|
|
15244
|
+
// lognow(`INFO : SERVER : Executing a onReceiveMessageListeners for messageType ${listenerMessageType}.`);
|
|
15245
|
+
|
|
15246
|
+
listener.execute(this, message, this.server, clientSocket);
|
|
15247
|
+
|
|
15248
|
+
},(listener)=>(message.type===listener.listenerMessageType));
|
|
15249
|
+
});
|
|
15250
|
+
|
|
15251
|
+
}
|
|
15252
|
+
|
|
15253
|
+
|
|
15254
|
+
/*private*/sendMessageToBlob(channelName, message,
|
|
15255
|
+
broadcastConfig={isOriginatingCell:false, destinationCellsOrigins:null, includeIncomingConnectionInTransmission:false, isRequest:false}){
|
|
15256
|
+
|
|
15257
|
+
const self=this;
|
|
15258
|
+
|
|
15259
|
+
if(broadcastConfig){
|
|
15260
|
+
if(broadcastConfig.isOriginatingCell){
|
|
15261
|
+
message.originatingCellOrigin=this.selfOrigin;
|
|
15262
|
+
}
|
|
15263
|
+
if(broadcastConfig.isRequest){
|
|
15264
|
+
message.isRequest=true;
|
|
15265
|
+
}
|
|
15266
|
+
if(broadcastConfig.destinationCellsOrigins && !empty(broadcastConfig.destinationCellsOrigins)){
|
|
15267
|
+
message.destinationCellsOrigins=broadcastConfig.destinationCellsOrigins;
|
|
15268
|
+
}
|
|
15269
|
+
}
|
|
15270
|
+
|
|
15271
|
+
if(!message.visitedCells)
|
|
15272
|
+
message.visitedCells=[];
|
|
15273
|
+
else if(contains(message.visitedCells,this.selfOrigin))
|
|
15274
|
+
return;
|
|
15275
|
+
message.visitedCells.push(this.selfOrigin);
|
|
15276
|
+
|
|
15277
|
+
if(broadcastConfig && broadcastConfig.includeIncomingConnectionInTransmission){
|
|
15278
|
+
foreach(this.incomingCells,(incomingCell)=>{
|
|
15279
|
+
// As a server, we send (forward) the message to the currently iterated upon client that is connected to us :
|
|
15280
|
+
incomingCell.server.send(channelName, message, null, incomingCell.clientSocket);
|
|
15281
|
+
},(incomingCell, incomingCellOrigin)=>
|
|
15282
|
+
(incomingCell.connected && !contains(message.visitedCells, incomingCellOrigin))
|
|
15283
|
+
);
|
|
15284
|
+
}
|
|
15285
|
+
|
|
15286
|
+
foreach(this.outcomingCells,(outcomingCell)=>{
|
|
15287
|
+
// As a client, we send (forward) the message to the currently iterated upon server we are connected to :
|
|
15288
|
+
outcomingCell.socketToServerClientInstance.send(channelName, message);
|
|
15289
|
+
},(outcomingCell, outcomingCellOrigin)=>
|
|
15290
|
+
(outcomingCell.connected && !contains(message.visitedCells, outcomingCellOrigin))
|
|
15291
|
+
);
|
|
15292
|
+
|
|
15293
|
+
|
|
15294
|
+
// We only add a result listener for the cell which sent the request message :
|
|
15295
|
+
if(message.isRequest && message.originatingCellOrigin==this.selfOrigin
|
|
15296
|
+
//&& (typeof(message.result)=="undefined" || message.result==null)
|
|
15297
|
+
){
|
|
15298
|
+
|
|
15299
|
+
const outletName="both";
|
|
15300
|
+
|
|
15301
|
+
// To emulate a promise-like behavior :
|
|
15302
|
+
let blobResponseListeners=this.onReceiveMessageListeners[outletName][channelName][message.type];
|
|
15303
|
+
if(blobResponseListeners && blobResponseListeners["forRequestsIssuedBySelf"]){
|
|
15304
|
+
// TRACE
|
|
15305
|
+
lognow(`INFO : Blob response «forRequestsIssuedBySelf» listener for cell ${this.selfOrigin} on channel ${channelName} for «${message.type}» already exists.`);
|
|
15306
|
+
return blobResponseListeners["forRequestsIssuedBySelf"];
|
|
15307
|
+
}else{
|
|
15308
|
+
blobResponseListeners=getOrCreateEmptyAttribute(this.onReceiveMessageListeners[outletName][channelName],message.type);
|
|
15309
|
+
}
|
|
15310
|
+
|
|
15311
|
+
const blobResponseListener={
|
|
15312
|
+
thenCallback:null,
|
|
15313
|
+
execute:(self, message, server, clientSocket)=>{
|
|
15314
|
+
// When we have received the message :
|
|
15315
|
+
this.thenCallback(message);
|
|
15316
|
+
|
|
15317
|
+
// We need to remove the result listener once it is completed though.
|
|
15318
|
+
delete blobResponseListeners["forRequestsIssuedBySelf"];
|
|
15319
|
+
},
|
|
15320
|
+
then:(thenCallback)=>{
|
|
15321
|
+
this.thenCallback=thenCallback;
|
|
15322
|
+
}
|
|
15323
|
+
};
|
|
15324
|
+
blobResponseListeners["forRequestsIssuedBySelf"]=blobResponseListener;
|
|
15325
|
+
|
|
15326
|
+
return blobResponseListener;
|
|
15327
|
+
}else{
|
|
15328
|
+
return null;
|
|
15329
|
+
}
|
|
15330
|
+
|
|
15331
|
+
}
|
|
15332
|
+
|
|
15333
|
+
|
|
15334
|
+
// ==========================================================
|
|
15335
|
+
// HELLO SEQUENCE
|
|
15336
|
+
|
|
15337
|
+
/*private*/initiateHelloSequence(socketToServerClientInstance){
|
|
15338
|
+
|
|
15339
|
+
// 1- We want to present ourselves to other outcoming cells :
|
|
15340
|
+
const helloRequest={
|
|
15341
|
+
originatingCellOrigin:this.selfOrigin,
|
|
15342
|
+
type:"helloRequest",
|
|
15343
|
+
};
|
|
15344
|
+
socketToServerClientInstance.send("protocol", helloRequest);
|
|
15345
|
+
}
|
|
15346
|
+
|
|
15347
|
+
|
|
15348
|
+
/*private*/handleHelloSequence(){
|
|
15349
|
+
|
|
15350
|
+
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
15351
|
+
getOrCreateEmptyAttribute(
|
|
15352
|
+
this.onReceiveMessageListeners["both"]["protocol"],"helloRequest")["forRequestsIssuedByOthers"]={
|
|
15353
|
+
execute:(selfParam, message, server, clientSocket)=>{
|
|
15354
|
+
|
|
15355
|
+
const cellOriginToCheck=message.originatingCellOrigin;
|
|
15356
|
+
|
|
15357
|
+
// TRACE
|
|
15358
|
+
lognow(`INFO : Incoming node ${cellOriginToCheck} has said hello. Updating its local information...`);
|
|
15359
|
+
|
|
15360
|
+
|
|
15361
|
+
selfParam.incomingCells[cellOriginToCheck]={
|
|
15362
|
+
connected:true,
|
|
15363
|
+
server:server,
|
|
15364
|
+
// DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
15365
|
+
// For this, use the server attribute (+ the clientSocket as argument) instead.
|
|
15366
|
+
clientSocket:clientSocket,
|
|
15367
|
+
};
|
|
15368
|
+
|
|
15369
|
+
},
|
|
15370
|
+
listenerMessageType:"helloRequest"
|
|
15371
|
+
};
|
|
15372
|
+
|
|
15373
|
+
}
|
|
15374
|
+
|
|
15375
|
+
|
|
15376
|
+
// ==========================================================
|
|
15377
|
+
// CELL IS READY SEQUENCE
|
|
15378
|
+
|
|
15379
|
+
/*private*/initiateCellIsReadySequence(){
|
|
15380
|
+
|
|
15381
|
+
// We update ourselve's status :
|
|
15382
|
+
this.cellsOverview[this.selfOrigin]={ready:true,startTime:this.startTime};
|
|
15383
|
+
|
|
15384
|
+
// INITIATE QUORUM IS REACHED SEQUENCE
|
|
15385
|
+
this.initiateQuorumIsReachedSequenceIfNecessary();
|
|
15386
|
+
|
|
15387
|
+
// We ask the others to update their status too :
|
|
15388
|
+
this.sendMessageToBlob("protocol",
|
|
15389
|
+
{type:"cellIsReady",startTime:this.startTime},
|
|
15390
|
+
{isOriginatingCell:true});
|
|
15391
|
+
}
|
|
15392
|
+
|
|
15393
|
+
/*private*/handleCellIsReadySequence(){
|
|
15394
|
+
getOrCreateEmptyAttribute(
|
|
15395
|
+
this.onReceiveMessageListeners["both"]["protocol"],"cellIsReady")["forRequestsIssuedByOthers"]={
|
|
15396
|
+
execute:(selfParam, message, server, clientSocket)=>{
|
|
15397
|
+
|
|
15398
|
+
selfParam.cellsOverview[message.originatingCellOrigin]={ready:true,startTime:message.startTime};
|
|
15399
|
+
|
|
15400
|
+
selfParam.sendMessageToBlob("protocol",message);
|
|
15401
|
+
|
|
15402
|
+
// // TRACE
|
|
15403
|
+
// lognow("INFO : Updated cells overview for this cell ("+selfParam.selfOrigin+") :",selfParam.cellsOverview);
|
|
15404
|
+
// lognow("DEBUG : selfParam.outcomingCells :",Object.keys(selfParam.outcomingCells));
|
|
15405
|
+
// lognow("DEBUG : selfParam.incomingCells :",Object.keys(selfParam.incomingCells));
|
|
15406
|
+
|
|
15407
|
+
// INITIATE QUORUM IS REACHED SEQUENCE
|
|
15408
|
+
selfParam.initiateQuorumIsReachedSequenceIfNecessary();
|
|
15409
|
+
|
|
15410
|
+
},
|
|
15411
|
+
listenerMessageType:"cellIsReady"
|
|
15412
|
+
};
|
|
15413
|
+
}
|
|
15414
|
+
|
|
15415
|
+
|
|
15416
|
+
|
|
15417
|
+
// ==========================================================
|
|
15418
|
+
// QUORUM IS REACHED SEQUENCE
|
|
15419
|
+
|
|
15420
|
+
/*private*/initiateQuorumIsReachedSequenceIfNecessary(){
|
|
15421
|
+
|
|
15422
|
+
if(getArraySize(this.cellsOverview) < this.quorumNumber) return;
|
|
15423
|
+
const lastStartedReadyCellOrigin=this.getLastStartedReadyCell();
|
|
15424
|
+
if(this.selfOrigin!=lastStartedReadyCellOrigin) return;
|
|
15425
|
+
if(this.quorumIsReachedSequenceIsInitiated) return;
|
|
15426
|
+
this.quorumIsReachedSequenceIsInitiated=true;
|
|
15427
|
+
|
|
15428
|
+
const quorumCells=copy(this.cellsOverview);
|
|
15429
|
+
|
|
15430
|
+
// TRACE
|
|
15431
|
+
lognow("INFO : Quorum is reached and this is the latest started cell. quorumCells :",quorumCells);
|
|
15432
|
+
|
|
15433
|
+
this.initializeModel(quorumCells);
|
|
15434
|
+
|
|
15435
|
+
|
|
15436
|
+
|
|
15437
|
+
}
|
|
15438
|
+
|
|
15439
|
+
/*private*/getLastStartedReadyCell(){
|
|
15440
|
+
return foreach(this.cellsOverview,(cell,cellOrigin)=>cellOrigin,null,(c1,c2)=>(c1.startTime==c2.startTime?0:(c1.startTime<c2.startTime?1:-1)));
|
|
15441
|
+
}
|
|
15442
|
+
|
|
15443
|
+
// ==========================================================
|
|
15444
|
+
// MODEL MANAGEMENT
|
|
15445
|
+
|
|
15446
|
+
/*private*/initializeModel(quorumCells){
|
|
15447
|
+
|
|
15448
|
+
const self=this;
|
|
15449
|
+
|
|
15450
|
+
const numberOfPartitions=getArraySize(quorumCells);
|
|
15451
|
+
|
|
15452
|
+
const controller=this.controller;
|
|
15453
|
+
const model=this.model;
|
|
15454
|
+
|
|
15455
|
+
|
|
15456
|
+
// We initialize the whole model :
|
|
15457
|
+
// (controller will handle if a persisted model exists)
|
|
15458
|
+
controller.initializeModelForAORTACNode(model);
|
|
15459
|
+
|
|
15460
|
+
// This must return the asked number of partitions, indexed by partition id :
|
|
15461
|
+
// CAUTION : The partition function MUST return ALL the objects in a partition, WHATEVER THEIR NESTING LEVEL !!!
|
|
15462
|
+
const modelPartitions=model.getPartitions(numberOfPartitions);
|
|
15463
|
+
|
|
15464
|
+
// We exclude this cell from the partition sending, because we already have the model partition at hand.
|
|
15465
|
+
this.removeLinks(modelPartitions[0].objects);
|
|
15466
|
+
this.doOnModelPartitionReception(modelPartitions[0]);
|
|
15467
|
+
|
|
15468
|
+
// We send the models objects to the required cells :
|
|
15469
|
+
let i=1;
|
|
15470
|
+
foreach(quorumCells, (quorumCell,quorumCellOrigin)=>{
|
|
15471
|
+
|
|
15472
|
+
const modelPartition=modelPartitions[i];
|
|
15473
|
+
self.removeLinks(modelPartition.objects);
|
|
15474
|
+
|
|
15475
|
+
const message=JSON.decycle({type:"modelPartition",partition:modelPartition});
|
|
15476
|
+
|
|
15477
|
+
// TRACE
|
|
15478
|
+
lognow(`INFO : Cell ${self.selfOrigin} is sending a partition to cell ${quorumCellOrigin}...`,message);
|
|
15479
|
+
|
|
15480
|
+
self.sendMessageToBlob("protocol", message,
|
|
15481
|
+
{isOriginatingCell:true, destinationCellsOrigins:[quorumCellOrigin], includeIncomingConnectionInTransmission:false, isRequest:false});
|
|
15482
|
+
|
|
15483
|
+
i++;
|
|
15484
|
+
},(quorumCell,quorumCellOrigin)=>quorumCellOrigin!=self.selfOrigin);
|
|
15485
|
+
|
|
15486
|
+
|
|
15487
|
+
// Each quorum member cell is responsible for a model partition
|
|
15488
|
+
// Then the sattelite cells will be handling the duplication
|
|
15489
|
+
|
|
15490
|
+
}
|
|
15491
|
+
|
|
15492
|
+
|
|
15493
|
+
// HANDLE PARTITION SEQUENCE
|
|
15494
|
+
|
|
15495
|
+
/*private*/handlePartitionSequence(){
|
|
15496
|
+
|
|
15497
|
+
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
15498
|
+
getOrCreateEmptyAttribute(
|
|
15499
|
+
this.onReceiveMessageListeners["both"]["protocol"],"modelPartition")["forRequestsIssuedByOthers"]={
|
|
15500
|
+
execute:(selfParam, messageParam, server, clientSocket)=>{
|
|
15501
|
+
|
|
15502
|
+
// If this cell is in the destinations of this message, we pass it along and then we do what it says :
|
|
15503
|
+
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
15504
|
+
|
|
15505
|
+
if(contains(messageParam.destinationCellsOrigins,selfParam.selfOrigin)){
|
|
15506
|
+
|
|
15507
|
+
const message=JSON.recycle(messageParam);
|
|
15508
|
+
|
|
15509
|
+
selfParam.doOnModelPartitionReception(message.partition);
|
|
15510
|
+
|
|
15511
|
+
}
|
|
15512
|
+
|
|
15513
|
+
},
|
|
15514
|
+
listenerMessageType:"modelPartition"
|
|
15515
|
+
};
|
|
15516
|
+
|
|
15517
|
+
}
|
|
15518
|
+
|
|
15519
|
+
|
|
15520
|
+
/*private*/doOnModelPartitionReception(partition){
|
|
15521
|
+
|
|
15522
|
+
// TRACE
|
|
15523
|
+
lognow(`INFO : Incoming partition for cell ${this.selfOrigin}. Updating local model...`,partition);
|
|
15524
|
+
lognow(`>>>>`,stringifyObject(JSON.decycle(partition.objects),1));
|
|
15525
|
+
|
|
15526
|
+
|
|
15527
|
+
|
|
15528
|
+
}
|
|
15529
|
+
|
|
15530
|
+
|
|
15531
|
+
/*private*/removeLinks(linkedObjects, currentObject=null){
|
|
15532
|
+
|
|
15533
|
+
const self=this;
|
|
15534
|
+
|
|
15535
|
+
if(!currentObject){
|
|
15536
|
+
foreach(linkedObjects, obj=>{
|
|
15537
|
+
self.removeLinksOnSingleObject(linkedObjects, obj);
|
|
15538
|
+
});
|
|
15539
|
+
}else{
|
|
15540
|
+
self.removeLinksOnSingleObject(linkedObjects, currentObject);
|
|
15541
|
+
}
|
|
15542
|
+
|
|
15543
|
+
return linkedObjects;
|
|
15544
|
+
}
|
|
15545
|
+
|
|
15546
|
+
/*private*/removeLinksOnSingleObject(linkedObjects, currentObject){
|
|
15547
|
+
|
|
15548
|
+
const self=this;
|
|
15549
|
+
foreach(currentObject, (attr,attrNameOrIndex)=>{
|
|
15550
|
+
// We only remove links to the objects not in the partition
|
|
15551
|
+
if(isClassObject(attr)){
|
|
15552
|
+
let aortacId=attr.aortacId;
|
|
15553
|
+
if(!aortacId){
|
|
15554
|
+
aortacId=getUUID();
|
|
15555
|
+
attr.aortacId=aortacId;
|
|
15556
|
+
}
|
|
15557
|
+
if(!contains(linkedObjects,attr)){
|
|
15558
|
+
currentObject[attrNameOrIndex]=aortacId+"@aortacId";
|
|
15559
|
+
// CAUTION : No need for recursive call here, because the partition function returns ALL the objects in a partition,
|
|
15560
|
+
// WHATEVER THEIR NESTING LEVEL !!!
|
|
15561
|
+
}
|
|
15562
|
+
}else{
|
|
15563
|
+
// However, we need a recursive call for any simple object or array that may reference a class object in another partition :
|
|
15564
|
+
self.removeLinksOnSingleObject(linkedObjects, attr);
|
|
15565
|
+
}
|
|
15566
|
+
},obj=>(isObject(obj) || isArray(obj)));
|
|
15567
|
+
|
|
15568
|
+
}
|
|
15569
|
+
|
|
15570
|
+
|
|
15571
|
+
// ******************************************************************
|
|
15572
|
+
|
|
15573
|
+
/*private*/collectDependencies(inputObjects){
|
|
15574
|
+
|
|
15575
|
+
////
|
|
15576
|
+
lognow("DEBUG : collectDependencies()...",inputObjects);
|
|
15577
|
+
|
|
15578
|
+
|
|
15579
|
+
}
|
|
15580
|
+
|
|
15581
|
+
/*private*/repercutChangesIfNeeded(inputObjects){
|
|
15582
|
+
|
|
15583
|
+
|
|
15584
|
+
////
|
|
15585
|
+
lognow("DEBUG : repercutChangesIfNeeded()...",inputObjects);
|
|
15586
|
+
|
|
15587
|
+
|
|
15588
|
+
}
|
|
15589
|
+
|
|
15590
|
+
|
|
15591
|
+
}
|
|
15592
|
+
|
|
15593
|
+
|
|
15594
|
+
|
|
15595
|
+
// ******************************************************************
|
|
15596
|
+
|
|
15597
|
+
// Public static hydration method :
|
|
15598
|
+
window.ao=(incompleteModelObjectToDecorate)=>{
|
|
15599
|
+
|
|
15600
|
+
const localServerCell=window.aortacCServerNodeInstance;
|
|
15601
|
+
if(!localServerCell){
|
|
15602
|
+
// TRACE
|
|
15603
|
+
lognow("ERROR : No AORTACCServerNode singleton instance. Doing nothing on the model object.");
|
|
15604
|
+
return incompleteModelObjectToDecorate;
|
|
15605
|
+
}
|
|
15606
|
+
|
|
15607
|
+
const liveModelObjects=localServerCell.liveModelObjects;
|
|
15608
|
+
|
|
15609
|
+
// First we clone the object, for its attriutes values information :
|
|
15610
|
+
const clonedObject=clone(incompleteModelObjectToDecorate);
|
|
15611
|
+
|
|
15612
|
+
// Then we replace all its methods :
|
|
15613
|
+
foreach(clonedObject, (method, methodName)=>{
|
|
15614
|
+
clonedObject[methodName]=new Proxy(method,
|
|
15615
|
+
{
|
|
15616
|
+
apply: function(methodToEnhance, thisArg, argumentsList) {
|
|
15617
|
+
|
|
15618
|
+
const inputObjects=[thisArg];
|
|
15619
|
+
inputObjects.push(...argumentsList);
|
|
15620
|
+
localServerCell.collectDependencies(inputObjects);
|
|
15621
|
+
|
|
15622
|
+
// // --- Treatment BEFORE function execution ---
|
|
15623
|
+
// console.log(`Calling function "${methodToEnhance.name}" with arguments: ${argumentsList}`);
|
|
15624
|
+
|
|
15625
|
+
// Call the original function (target) with its intended 'this' context (thisArg)
|
|
15626
|
+
// and arguments (argumentsList) using Reflect.apply
|
|
15627
|
+
const result = Reflect.apply(methodToEnhance, thisArg, argumentsList);
|
|
15628
|
+
|
|
15629
|
+
// // --- Treatment AFTER function execution ---
|
|
15630
|
+
// console.log(`Function "${methodToEnhance.name}" returned: ${result}`);
|
|
15631
|
+
|
|
15632
|
+
inputObjects.push(result);
|
|
15633
|
+
localServerCell.repercutChangesIfNeeded(inputObjects);
|
|
15634
|
+
|
|
15635
|
+
return result;
|
|
15636
|
+
},
|
|
15637
|
+
}
|
|
15638
|
+
);
|
|
15639
|
+
},attribute=>isFunction(attribute));
|
|
15640
|
+
|
|
15641
|
+
return clonedObject;
|
|
15642
|
+
};
|
|
15643
|
+
|
|
15644
|
+
|
|
15645
|
+
|
|
15646
|
+
window.getAORTACServerNode=function(quorumNumber=1,selfOrigin="ws://127.0.0.1:40000", outcomingCellsOrigins=[], model, controller){
|
|
15647
|
+
//return new AORTACServerNode("node_"+getUUID("short"), selfOrigin, outcomingCellsOrigins, model, controller);
|
|
15648
|
+
if(window.aortacCServerNodeInstance){
|
|
15649
|
+
// TRACE
|
|
15650
|
+
throw new Error("ERROR : The AORTACCServerNode singleton instance already exists. It cannot be instantiated again in the same process. Aborting.");
|
|
15651
|
+
}
|
|
15652
|
+
window.aortacCServerNodeInstance=new AORTACServerCell(quorumNumber, selfOrigin, outcomingCellsOrigins, model, controller);
|
|
15653
|
+
return window.aortacCServerNodeInstance;
|
|
15654
|
+
}
|
|
15655
|
+
|
|
15656
|
+
|
|
15657
|
+
|
|
15658
|
+
|
|
15659
|
+
|
|
15660
|
+
|
|
15661
|
+
|
|
15662
|
+
// ==================================================================================================================
|
|
15663
|
+
|
|
15664
|
+
|
|
15665
|
+
// AORTAC CLIENT
|
|
15666
|
+
|
|
15667
|
+
|
|
15668
|
+
|
|
15669
|
+
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
15670
|
+
|
|
15671
|
+
// New implementation :
|
|
15672
|
+
|
|
15673
|
+
class AORTACClientCell{
|
|
15674
|
+
|
|
15675
|
+
constructor(serverNodeOrigin, model, view, isNodeContext=false){
|
|
15676
|
+
|
|
15677
|
+
}
|
|
15678
|
+
|
|
15679
|
+
}
|
|
15680
|
+
|
|
15681
|
+
|
|
15682
|
+
|
|
15683
|
+
|
|
15684
|
+
window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=false){
|
|
15685
|
+
//return new AORTACClient("client_"+getUUID(), serverNodeOrigin, model, view, isNodeContext);
|
|
15686
|
+
return new AORTACClientCell(serverNodeOrigin, model, view, isNodeContext);
|
|
15687
|
+
}
|
|
15688
|
+
|
|
15689
|
+
|
|
15690
|
+
|
|
15691
|
+
|
|
15692
|
+
/* ## Utility global methods in a javascript, console (nodejs) or vanilla javascript with no browser environment.
|
|
15693
|
+
*
|
|
15694
|
+
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
15695
|
+
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
15696
|
+
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
15697
|
+
*
|
|
15698
|
+
* # Library name : «aotrautils»
|
|
15699
|
+
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
15700
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
15701
|
+
* # Author email : info@alqemia.com
|
|
15702
|
+
* # Organization name : Alqemia
|
|
15703
|
+
* # Organization email : admin@alqemia.com
|
|
15704
|
+
* # Organization website : https://alqemia.com
|
|
15705
|
+
*
|
|
15706
|
+
*
|
|
15707
|
+
*/
|
|
15708
|
+
|
|
15709
|
+
|
|
15710
|
+
|
|
15711
|
+
// COMPATIBILITY browser javascript / nodejs environment :
|
|
15712
|
+
if(typeof(window)==="undefined") window=global;
|
|
15713
|
+
|
|
15714
|
+
|
|
15715
|
+
|
|
15716
|
+
|
|
15717
|
+
// OLD : socket.io :
|
|
15718
|
+
// https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
|
|
15719
|
+
// https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
|
|
15720
|
+
// https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
|
|
15721
|
+
// https://socket.io/docs/v4/client-socket-instance/
|
|
15722
|
+
|
|
15723
|
+
// NEW : ws :
|
|
15724
|
+
// https://github.com/websockets/ws#installing
|
|
15725
|
+
// https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
|
|
15726
|
+
// ON BROWSER SIDE : Native Websockets :
|
|
15727
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
15728
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
|
|
15729
|
+
|
|
15730
|
+
// We have to import both implementations, regardless of which one is chosen :
|
|
15731
|
+
//Socket=require("socket.io");
|
|
15732
|
+
//WebSocket=require("ws");
|
|
15733
|
+
|
|
15734
|
+
|
|
15735
|
+
|
|
15736
|
+
|
|
15737
|
+
// =================================================================================
|
|
15738
|
+
// NODEJS UTILS
|
|
15739
|
+
|
|
15740
|
+
const FILE_ENCODING="utf8";
|
|
15741
|
+
|
|
15742
|
+
const ADD_CORS_HEADER=true;
|
|
15743
|
+
|
|
15744
|
+
|
|
15745
|
+
|
|
15746
|
+
if(typeof(require)!="undefined" && typeof(fs)!="undefined" )
|
|
15747
|
+
window.fs=require("fs");
|
|
15748
|
+
|
|
15749
|
+
|
|
15750
|
+
|
|
15751
|
+
|
|
15752
|
+
// Nodejs filesystem utils :
|
|
15753
|
+
if(typeof(fs)==="undefined"){
|
|
15754
|
+
// TRACE
|
|
15755
|
+
console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
|
|
15756
|
+
getPersister=()=>{ return null; };
|
|
15757
|
+
|
|
15758
|
+
}else{
|
|
15759
|
+
|
|
15760
|
+
|
|
15761
|
+
getPersister=function(dataDirPath,prefix=""){
|
|
15762
|
+
|
|
15763
|
+
let self={
|
|
15764
|
+
|
|
15765
|
+
dataDirPath:dataDirPath,
|
|
15766
|
+
prefix:prefix,
|
|
15767
|
+
|
|
15768
|
+
//FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
|
|
15769
|
+
/*private*/getPath:function(clientId="noclient", repositoryName="norepository"){
|
|
15770
|
+
// let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
|
|
15771
|
+
let path=`${self.dataDirPath}`
|
|
15772
|
+
+ (blank(self.prefix)?"":(self.prefix+"."))
|
|
15773
|
+
+`${clientId}.${repositoryName}.json`;
|
|
15774
|
+
return path;
|
|
15775
|
+
},
|
|
15776
|
+
|
|
15777
|
+
readTreeObjectFromFile:function(clientId="noclient", repositoryName="norepository", keepClassName=false){
|
|
15778
|
+
|
|
15779
|
+
let path=self.getPath(clientId,repositoryName);
|
|
15780
|
+
let resultFlat=null;
|
|
15781
|
+
|
|
15782
|
+
try{
|
|
15783
|
+
|
|
15784
|
+
resultFlat=fs.readFileSync(path, FILE_ENCODING);
|
|
15785
|
+
|
|
15786
|
+
}catch(error){
|
|
15787
|
+
// TRACE
|
|
15788
|
+
console.log("ERROR : Could not read file «"+path+"».");
|
|
15789
|
+
|
|
15790
|
+
return null;
|
|
15791
|
+
}
|
|
15137
15792
|
|
|
15138
15793
|
|
|
15139
15794
|
let resultData={};
|
|
@@ -15153,8 +15808,6 @@ if(typeof(fs)==="undefined"){
|
|
|
15153
15808
|
|
|
15154
15809
|
saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
|
|
15155
15810
|
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
15811
|
if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
|
|
15159
15812
|
dataFlatForClient=getAsFlatStructure(dataFlatForClient);
|
|
15160
15813
|
}
|
|
@@ -15170,7 +15823,6 @@ if(typeof(fs)==="undefined"){
|
|
|
15170
15823
|
|
|
15171
15824
|
|
|
15172
15825
|
let path=self.getPath(clientId,repositoryName);
|
|
15173
|
-
|
|
15174
15826
|
fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
|
|
15175
15827
|
if(error){
|
|
15176
15828
|
// TRACE
|
|
@@ -15190,6 +15842,16 @@ if(typeof(fs)==="undefined"){
|
|
|
15190
15842
|
}
|
|
15191
15843
|
|
|
15192
15844
|
|
|
15845
|
+
window.fileExists=(filePath)=>{
|
|
15846
|
+
if(typeof(fs)=="undefined"){
|
|
15847
|
+
// TRACE
|
|
15848
|
+
lognow("ERROR : «fs» node dependency is not available ! Cannot test if file exists.");
|
|
15849
|
+
return null;
|
|
15850
|
+
}
|
|
15851
|
+
return fs.existsSync(filePath);
|
|
15852
|
+
};
|
|
15853
|
+
|
|
15854
|
+
|
|
15193
15855
|
|
|
15194
15856
|
// Nodejs server launching helper functions :
|
|
15195
15857
|
//Networking management :
|
|
@@ -15267,6 +15929,47 @@ getConsoleParam=function(index=0, argsOffset=0){
|
|
|
15267
15929
|
|
|
15268
15930
|
|
|
15269
15931
|
|
|
15932
|
+
window.getConsoleCLI=(doOnCommands={"makeSandiwch":()=>{}}, promptText="Enter command> ")=>{
|
|
15933
|
+
|
|
15934
|
+
const readline = require("node:readline");
|
|
15935
|
+
|
|
15936
|
+
const cliInterface = readline.createInterface({
|
|
15937
|
+
input: process.stdin,
|
|
15938
|
+
output: process.stdout,
|
|
15939
|
+
prompt: nonoull(promptText,"Enter command> ")
|
|
15940
|
+
});
|
|
15941
|
+
|
|
15942
|
+
cliInterface.prompt();
|
|
15943
|
+
|
|
15944
|
+
cliInterface.on("line", (line) => {
|
|
15945
|
+
const input = line.trim();
|
|
15946
|
+
switch (input) {
|
|
15947
|
+
case "quit":
|
|
15948
|
+
console.log("Bye!");
|
|
15949
|
+
cliInterface.close();
|
|
15950
|
+
break;
|
|
15951
|
+
case "help":
|
|
15952
|
+
console.log("Available commands: quit, help and :", Object.keys(doOnCommands));
|
|
15953
|
+
break;
|
|
15954
|
+
default:
|
|
15955
|
+
if(doOnCommands) doOnCommands[input]();
|
|
15956
|
+
break;
|
|
15957
|
+
}
|
|
15958
|
+
|
|
15959
|
+
// Show the prompt again :
|
|
15960
|
+
cliInterface.prompt();
|
|
15961
|
+
|
|
15962
|
+
}).on("close", () => {
|
|
15963
|
+
process.exit(0);
|
|
15964
|
+
});
|
|
15965
|
+
|
|
15966
|
+
return cliInterface
|
|
15967
|
+
};
|
|
15968
|
+
|
|
15969
|
+
|
|
15970
|
+
|
|
15971
|
+
|
|
15972
|
+
|
|
15270
15973
|
// NODE ONLY SERVER / CLIENTS :
|
|
15271
15974
|
WebsocketImplementation={
|
|
15272
15975
|
|
|
@@ -15409,9 +16112,8 @@ WebsocketImplementation={
|
|
|
15409
16112
|
|
|
15410
16113
|
// Join room server part protocol :
|
|
15411
16114
|
nodeServerInstance.receive("protocol",(message, clientSocket)=>{
|
|
15412
|
-
if(message.type!=="joinRoom" || !clientSocket) return;
|
|
15413
16115
|
nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
|
|
15414
|
-
});
|
|
16116
|
+
},{listenerMessageType:"joinRoom"});
|
|
15415
16117
|
|
|
15416
16118
|
// To make the server aware of the clients connections states :
|
|
15417
16119
|
nodeServerInstance.serverSocket.on("close", function close() {
|
|
@@ -15465,6 +16167,12 @@ WebsocketImplementation={
|
|
|
15465
16167
|
rejectUnauthorized:false, // (THIS IS A KNOWN SECURITY BREACH)
|
|
15466
16168
|
secure: isSecure
|
|
15467
16169
|
});
|
|
16170
|
+
|
|
16171
|
+
clientSocket.addEventListener("error", error=>{
|
|
16172
|
+
// TRACE
|
|
16173
|
+
lognow("ERROR : (NODEJS) A WebSocket client error occurred while trying to connect to server:", error.message);
|
|
16174
|
+
});
|
|
16175
|
+
|
|
15468
16176
|
}else{
|
|
15469
16177
|
// NOW : socket.io :
|
|
15470
16178
|
//client on server-side:
|
|
@@ -15475,6 +16183,12 @@ WebsocketImplementation={
|
|
|
15475
16183
|
// NO : clientSocket=new Socket.Client(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
15476
16184
|
clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure, autoConnect:true});
|
|
15477
16185
|
// UNUSEFUL (since we have the autoconnect:true option) : clientSocket.connect();
|
|
16186
|
+
|
|
16187
|
+
clientSocket.on("connect_error", error=>{
|
|
16188
|
+
// TRACE
|
|
16189
|
+
lognow("ERROR : (NODEJS) A SocketIO client error occurred while trying to connect to server:", error.message);
|
|
16190
|
+
});
|
|
16191
|
+
|
|
15478
16192
|
}
|
|
15479
16193
|
}
|
|
15480
16194
|
|
|
@@ -15508,10 +16222,22 @@ WebsocketImplementation={
|
|
|
15508
16222
|
// - FIRST GO TO THE HTTPS:// SERVER ADDRESS WITH BROWSER
|
|
15509
16223
|
// - THEN ADD THE SECURITY EXCEPTION IN THE BROWSER !
|
|
15510
16224
|
clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
|
|
16225
|
+
|
|
16226
|
+
clientSocket.addEventListener("error", error=>{
|
|
16227
|
+
// TRACE
|
|
16228
|
+
lognow("ERROR : (BROWSER) A WebSocket client error occurred while trying to connect to server:", error.message);
|
|
16229
|
+
});
|
|
16230
|
+
|
|
15511
16231
|
}else if(typeof(io)!=="undefined"){
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
16232
|
+
// OLD SYNTAX :clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
16233
|
+
// ALTERNATIVE :
|
|
16234
|
+
clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
16235
|
+
|
|
16236
|
+
clientSocket.on("connect_error", error=>{
|
|
16237
|
+
// TRACE
|
|
16238
|
+
lognow("ERROR : (BROWSER) A SocketIO client error occurred while trying to connect to server:", error.message);
|
|
16239
|
+
});
|
|
16240
|
+
|
|
15515
16241
|
}
|
|
15516
16242
|
|
|
15517
16243
|
// BROWSER CLIENT INSTANCE :
|
|
@@ -15817,7 +16543,7 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
15817
16543
|
// DOES NOT WORK : USE Java FusrodaServer instead :
|
|
15818
16544
|
//
|
|
15819
16545
|
///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
|
|
15820
|
-
//createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=
|
|
16546
|
+
//createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=4000){
|
|
15821
16547
|
//
|
|
15822
16548
|
// // https://www.npmjs.com/package/screenshot-desktop
|
|
15823
16549
|
// // https://github.com/octalmage/robotjs
|
|
@@ -15854,7 +16580,7 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
15854
16580
|
//
|
|
15855
16581
|
// screenshot().then((img) => {
|
|
15856
16582
|
//
|
|
15857
|
-
// const data={image:img,
|
|
16583
|
+
// const data={image:img,listenerMessageType:"imageData"};
|
|
15858
16584
|
// serverParam.send("message", data, "clients");
|
|
15859
16585
|
//
|
|
15860
16586
|
// serverParam.isScreenshotStarted=false;
|
|
@@ -15876,7 +16602,7 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
15876
16602
|
// // /*DO NOTHING*/
|
|
15877
16603
|
// // };
|
|
15878
16604
|
// // const server={};
|
|
15879
|
-
// // server.start=(port=
|
|
16605
|
+
// // server.start=(port=4000)=>{
|
|
15880
16606
|
// // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
|
|
15881
16607
|
// // };
|
|
15882
16608
|
//
|
|
@@ -16278,871 +17004,287 @@ appendGetParameters=function(apiURL, namedArgsParam){
|
|
|
16278
17004
|
|
|
16279
17005
|
|
|
16280
17006
|
|
|
16281
|
-
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
16282
17007
|
|
|
16283
|
-
AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT=30000;
|
|
16284
|
-
REQUESTS_IDS_HISTORY_SIZE=10;
|
|
16285
17008
|
|
|
16286
|
-
class AORTACNode{
|
|
16287
|
-
|
|
16288
|
-
constructor(nodeId, selfOrigin="127.0.0.1:40000",outcomingNodesOrigins=[], model, controller, allowAnonymousNodes=true, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
|
|
16289
|
-
|
|
16290
|
-
this.nodeId=nodeId;
|
|
16291
|
-
this.selfOrigin=selfOrigin;
|
|
16292
|
-
this.sslConfig=sslConfig;
|
|
16293
|
-
this.outcomingNodesOrigins=outcomingNodesOrigins;
|
|
16294
|
-
this.allowAnonymousNodes=allowAnonymousNodes;
|
|
16295
|
-
|
|
16296
|
-
this.model=model; // must implement functions
|
|
16297
|
-
this.controller=controller; // Must implement functions
|
|
16298
|
-
|
|
16299
|
-
this.server=null;
|
|
16300
|
-
this.clients={};
|
|
16301
|
-
this.incomingServers={};
|
|
16302
|
-
this.outcomingServers={};
|
|
16303
|
-
this.authorizedNodesIds=[this.nodeId];
|
|
16304
|
-
|
|
16305
|
-
this.listeners={"protocol":[],"cluster":[],"neighbors":[],"inputs":[]};
|
|
16306
|
-
|
|
16307
|
-
this.executedRequestIdsHistory=[];
|
|
16308
|
-
|
|
16309
|
-
this.isConnectedToCluster=false;
|
|
16310
|
-
|
|
16311
|
-
this.modelObjectsDirectory={};
|
|
16312
|
-
this.objectsIndexesForClients={};
|
|
16313
17009
|
|
|
16314
|
-
this.addProtocolListeners();
|
|
16315
17010
|
|
|
16316
|
-
// TRACE
|
|
16317
|
-
lognow(`AORTAC node created with id ${this.nodeId}.`);
|
|
16318
|
-
}
|
|
16319
|
-
|
|
16320
|
-
start(){
|
|
16321
17011
|
|
|
16322
|
-
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
|
|
16330
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
|
|
16338
|
-
// TRACE
|
|
16339
|
-
console.log("INFO : SERVER : Client or incoming node has sent a "+channelName+" message: ", message);
|
|
16340
|
-
foreach(self.listeners[channelName], listener => {
|
|
16341
|
-
listener.execute(self, message, self.server, clientSocket);
|
|
16342
|
-
},(listener)=>(message.type===listener.messageType));
|
|
16343
|
-
|
|
16344
|
-
});
|
|
16345
|
-
});
|
|
16346
|
-
|
|
16347
|
-
// OLD : self.connectToOutcomingServers(self, server).then((server)=>{ self.doOnConnectedToCluster(); });
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
}, port, this.sslConfig.certPath, this.sslConfig.keyPath);
|
|
16351
|
-
|
|
16352
|
-
this.server.serverManager.start();
|
|
16353
|
-
|
|
16354
|
-
// TRACE
|
|
16355
|
-
lognow(`AORTAC node started with id ${this.nodeId}.`);
|
|
17012
|
+
/* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.
|
|
17013
|
+
*
|
|
17014
|
+
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
17015
|
+
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
17016
|
+
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
17017
|
+
*
|
|
17018
|
+
* # Library name : «aotrautils»
|
|
17019
|
+
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
17020
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
17021
|
+
* # Author email : info@alqemia.com
|
|
17022
|
+
* # Organization name : Alqemia
|
|
17023
|
+
* # Organization email : admin@alqemia.com
|
|
17024
|
+
* # Organization website : https://alqemia.com
|
|
17025
|
+
*
|
|
17026
|
+
*
|
|
17027
|
+
*/
|
|
16356
17028
|
|
|
16357
17029
|
|
|
16358
|
-
return this;
|
|
16359
|
-
}
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
connect(){
|
|
16363
|
-
|
|
16364
|
-
const self=this;
|
|
16365
|
-
self.connectToOutcomingServers(self).then((server)=>{ self.doOnConnectedToCluster(); });
|
|
16366
17030
|
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
/*private*/addProtocolListeners(){
|
|
16374
|
-
|
|
16375
|
-
const self=this;
|
|
16376
|
-
// ============== OTHER SERVER / CLIENT REGISTRATION PHASE ==============
|
|
16377
|
-
// - OTHER SERVER REGISTRATION PHASE :
|
|
16378
|
-
this.listeners["protocol"].push({
|
|
16379
|
-
messageType:"request.register.server.incoming",
|
|
16380
|
-
execute:(self, message, server, clientSocket)=>{
|
|
16381
|
-
const incomingServerNodeId=message.nodeId;
|
|
16382
|
-
|
|
16383
|
-
// TRACE
|
|
16384
|
-
lognow(` (${self.nodeId}) Receiving registering request from node ${incomingServerNodeId}...`);
|
|
16385
|
-
|
|
16386
|
-
if(!self.allowAnonymousNodes && self.isInAuthorizedNodes(incomingServerNodeId)){
|
|
16387
|
-
// TRACE
|
|
16388
|
-
lognow("WARN : Cannot accept incoming server with node id «"+incomingServerNodeId+"» because it's not in the authorized nodes list.");
|
|
16389
|
-
return;
|
|
16390
|
-
}
|
|
16391
|
-
if(!contains(self.authorizedNodesIds,incomingServerNodeId)) self.authorizedNodesIds.push(incomingServerNodeId);
|
|
16392
|
-
|
|
16393
|
-
// TRACE
|
|
16394
|
-
lognow(` (${self.nodeId}) Adding registering node ${incomingServerNodeId} to incoming servers...`);
|
|
16395
|
-
|
|
16396
|
-
self.incomingServers[incomingServerNodeId]={clientSocket:clientSocket};
|
|
16397
|
-
|
|
16398
|
-
const welcomeResponse={
|
|
16399
|
-
nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
|
|
16400
|
-
type:"response.register.server.incoming",
|
|
16401
|
-
authorizedNodesIds:self.authorizedNodesIds
|
|
16402
|
-
};
|
|
16403
|
-
server.send("protocol", welcomeResponse, null, clientSocket);
|
|
16404
|
-
|
|
16405
|
-
// TRACE
|
|
16406
|
-
lognow(` (${self.nodeId}) Sent registering response to node ${incomingServerNodeId}.`);
|
|
16407
|
-
}
|
|
16408
|
-
});
|
|
16409
|
-
|
|
16410
|
-
// - CLIENT REGISTRATION PHASE : (for clients to the server, NOT INCOMING NODES !)
|
|
16411
|
-
this.handleClients();
|
|
16412
|
-
|
|
16413
|
-
}
|
|
16414
|
-
|
|
16415
|
-
/*private*/handleClients(){
|
|
16416
|
-
const self=this;
|
|
16417
|
-
|
|
16418
|
-
// --------- PROTOCOL HANDLING ---------
|
|
16419
|
-
|
|
16420
|
-
this.listeners["protocol"].push({
|
|
16421
|
-
messageType:"request.register.client",
|
|
16422
|
-
execute:(self, message, server, clientSocket)=>{
|
|
16423
|
-
const clientId=message.clientId;
|
|
16424
|
-
|
|
16425
|
-
self.clients[clientId]={clientSocket:clientSocket};
|
|
17031
|
+
// COMPATIBILITY browser javascript / nodejs environment :
|
|
17032
|
+
if(typeof(window)==="undefined") window=global;
|
|
16426
17033
|
|
|
16427
|
-
// TRACE
|
|
16428
|
-
lognow(` (${self.nodeId}) Receiving non-cluster client registration request from client ${clientId}.`);
|
|
16429
|
-
|
|
16430
|
-
const welcomeClientResponse={
|
|
16431
|
-
nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
|
|
16432
|
-
type:"response.register.client",
|
|
16433
|
-
// If necessary we also send them our model part :
|
|
16434
|
-
partialModelString:(message.isReferenceNode?null:JSON.stringifyDecycle(self.model)),
|
|
16435
|
-
isReferenceNode:(!!message.isReferenceNode),
|
|
16436
|
-
};
|
|
16437
|
-
server.send("protocol", welcomeClientResponse, null, clientSocket);
|
|
16438
|
-
}
|
|
16439
|
-
});
|
|
16440
|
-
|
|
16441
|
-
this.listeners["protocol"].push({
|
|
16442
|
-
messageType:"request.unregister.client",
|
|
16443
|
-
execute:(self, message, server, clientSocket)=>{
|
|
16444
|
-
const clientId=message.clientId;
|
|
16445
|
-
|
|
16446
|
-
delete self.clients[clientId];
|
|
16447
17034
|
|
|
16448
|
-
// TRACE
|
|
16449
|
-
lognow(` (${self.nodeId}) Receiving non-cluster client unregistration request from client ${clientId}.`);
|
|
16450
|
-
|
|
16451
|
-
const unwelcomeClientResponse={
|
|
16452
|
-
nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
|
|
16453
|
-
type:"response.unregister.client",
|
|
16454
|
-
};
|
|
16455
|
-
server.send("protocol", unwelcomeClientResponse, null, clientSocket);
|
|
16456
|
-
}
|
|
16457
|
-
});
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
this.listeners["protocol"].push({
|
|
16461
|
-
messageType:"request.interrogation.client",
|
|
16462
|
-
execute:(self, message, server, clientSocket)=>{
|
|
16463
|
-
const clientId=message.clientId;
|
|
16464
|
-
|
|
16465
|
-
// TRACE
|
|
16466
|
-
lognow(` (${self.nodeId}) Receiving non-cluster client interrogation request from client ${clientId}.`);
|
|
16467
|
-
|
|
16468
|
-
const clientBoundaries=message.boundaries;
|
|
16469
|
-
|
|
16470
|
-
// We ask the whole cluster to find the requested objects :
|
|
16471
|
-
const modelSeekObjectsRequest={
|
|
16472
|
-
type:"request.model.seekObjects",
|
|
16473
|
-
originatingClientId:clientId,
|
|
16474
|
-
clientBoundaries:clientBoundaries,
|
|
16475
|
-
};
|
|
16476
|
-
this.propagateToCluster(modelSeekObjectsRequest);
|
|
16477
17035
|
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
}
|
|
16481
|
-
});
|
|
16482
|
-
|
|
16483
|
-
// --------- INPUTS HANDLING ---------
|
|
16484
|
-
|
|
16485
|
-
this.listeners["inputs"].push({
|
|
16486
|
-
messageType:"request.inputs.client",
|
|
16487
|
-
execute:(self, message, server, clientSocket)=>{
|
|
16488
|
-
const clientId=message.clientId;
|
|
16489
|
-
|
|
16490
|
-
// TRACE
|
|
16491
|
-
lognow(` (${self.nodeId}) Receiving non-cluster client inputs request from client ${clientId}.`);
|
|
16492
|
-
|
|
16493
|
-
const clientInputs=message.inputs;
|
|
16494
|
-
const clientSubBoundaries=message.subBoundaries;
|
|
17036
|
+
// ==================================================================================================================
|
|
16495
17037
|
|
|
16496
|
-
|
|
16497
|
-
// We let the controller interpret these inputs :
|
|
16498
|
-
const modifiedObjects=self.controller.interpretInputs(clientInputs, clientSubBoundaries);
|
|
16499
|
-
|
|
16500
17038
|
|
|
16501
|
-
|
|
16502
|
-
|
|
16503
|
-
|
|
17039
|
+
class ClientReceptionEntryPoint{
|
|
17040
|
+
|
|
17041
|
+
constructor(channelNameParam, entryPointId, clientsRoomsTag, listenerConfig, doOnIncomingMessage){
|
|
17042
|
+
this.channelName=channelNameParam;
|
|
17043
|
+
this.entryPointId=entryPointId;
|
|
17044
|
+
this.clientsRoomsTag=clientsRoomsTag;
|
|
17045
|
+
this.listenerConfig=listenerConfig;
|
|
17046
|
+
this.doOnIncomingMessage=doOnIncomingMessage;
|
|
16504
17047
|
}
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
const self=this;
|
|
17048
|
+
|
|
17049
|
+
// III-
|
|
17050
|
+
execute(eventOrMessage, clientSocket, clientReceptionEntryPoints){
|
|
16510
17051
|
|
|
16511
|
-
|
|
16512
|
-
// Adding propagation listener :
|
|
16513
|
-
this.setPropagation("cluster","request.model.seekObjects",(self, message, server, clientSocket)=>{
|
|
16514
|
-
|
|
16515
|
-
// If we receive this request from cluster, then we try to find the requested objects
|
|
16516
|
-
const relayNodeid=message.nodeId;
|
|
16517
|
-
const originatingClientId=message.originatingClientId;
|
|
16518
|
-
const clientBoundaries=message.clientBoundaries
|
|
16519
|
-
const objectsIds=self.model.getObjectsIdsWithinBoundaries(clientBoundaries);
|
|
16520
|
-
|
|
16521
|
-
// If server has not the seeked objects, it answers with an empty array.
|
|
16522
|
-
|
|
16523
|
-
// TRACE
|
|
16524
|
-
lognow(`(${self.nodeId}) Node receiving a seek objects request from relay node ${message.nodeId}...objectsIds=`,objectsIds);
|
|
16525
|
-
|
|
17052
|
+
if(!eventOrMessage){
|
|
16526
17053
|
// TRACE
|
|
16527
|
-
lognow(
|
|
16528
|
-
|
|
16529
|
-
|
|
16530
|
-
type:"response.model.seekObject",
|
|
16531
|
-
originatingClientId:originatingClientId,
|
|
16532
|
-
clientBoundaries:clientBoundaries,
|
|
16533
|
-
objectsIds:objectsIds,
|
|
16534
|
-
nodeServerInfo:self.selfOrigin
|
|
16535
|
-
};
|
|
16536
|
-
this.propagateToCluster(modelObjectsSeekResponse, relayNodeid/*destination node*/);
|
|
17054
|
+
lognow("ERROR : No message received.");
|
|
17055
|
+
return;
|
|
17056
|
+
}
|
|
16537
17057
|
|
|
16538
|
-
|
|
16539
|
-
|
|
16540
|
-
// Adding propagation listener :
|
|
16541
|
-
this.setPropagation("cluster","response.model.seekObject",(self, message, server, clientSocket)=>{
|
|
16542
|
-
|
|
16543
|
-
// Here the relay server gathers the information from the other nodes, and sends it back to client ;
|
|
16544
|
-
|
|
16545
|
-
const referenceNodeId=self.nodeId;
|
|
16546
|
-
const originatingClientId=message.originatingClientId;
|
|
16547
|
-
const originatingNodeId=message.nodeId;
|
|
16548
|
-
const clientBoundaries=message.clientBoundaries;
|
|
16549
|
-
const objectsIds=message.objectsIds;
|
|
16550
|
-
const nodeServerInfo=message.nodeServerInfo;
|
|
16551
|
-
|
|
17058
|
+
if(!clientSocket){
|
|
16552
17059
|
// TRACE
|
|
16553
|
-
lognow(
|
|
16554
|
-
|
|
16555
|
-
|
|
16556
|
-
const objectsIndexForClient=self.objectsIndexesForClients[originatingClientId];
|
|
16557
|
-
objectsIndexForClient[originatingNodeId]={nodeServerInfo:nodeServerInfo, objectsIds:objectsIds};
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
// We check if all known nodes have answered :
|
|
16561
|
-
if(getArraySize(self.authorizedNodesIds)-1/*Because we exclude the reference node*/<=getArraySize(objectsIndexForClient)){
|
|
16562
|
-
|
|
16563
|
-
// The reference node also answers to the request to its direct client :
|
|
16564
|
-
const objectsIdsFromReferenceNode=self.model.getObjectsIdsWithinBoundaries(clientBoundaries);
|
|
16565
|
-
const referenceNodeServerInfo=self.selfOrigin;
|
|
16566
|
-
objectsIndexForClient[referenceNodeId]={nodeServerInfo:referenceNodeServerInfo, objectsIds:objectsIdsFromReferenceNode};
|
|
16567
|
-
|
|
16568
|
-
const client=self.clients[originatingClientId];
|
|
16569
|
-
const clientSocket=client.clientSocket;
|
|
16570
|
-
|
|
16571
|
-
// TRACE
|
|
16572
|
-
lognow(`(${self.nodeId}) Sending objects ids to the client ${originatingClientId}...objectsIndexForClient=`,objectsIndexForClient);
|
|
16573
|
-
|
|
16574
|
-
// We filter all the index entries with an empty objetcs ids array :
|
|
16575
|
-
const objectsIndexForClientWithoutEmptyObjects={};
|
|
16576
|
-
foreach(objectsIndexForClient,(nodeInfoAndObjectsIds, serverNodeId)=>{
|
|
16577
|
-
objectsIndexForClientWithoutEmptyObjects[serverNodeId]=nodeInfoAndObjectsIds;
|
|
16578
|
-
},(nodeInfoAndObjectsIds,serverNodeId)=>(!empty(nodeInfoAndObjectsIds.objectsIds)));
|
|
16579
|
-
|
|
16580
|
-
// We send the final result information (objects ids) to client :
|
|
16581
|
-
const interrogationClientResponse={
|
|
16582
|
-
nodeId:self.nodeId,
|
|
16583
|
-
type:"response.interrogation.client",
|
|
16584
|
-
serversNodesIdsForModelObjectsForClient:objectsIndexForClientWithoutEmptyObjects
|
|
16585
|
-
};
|
|
16586
|
-
self.server.send("protocol", interrogationClientResponse, null, clientSocket);
|
|
16587
|
-
}
|
|
16588
|
-
|
|
16589
|
-
});
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
// ------------------------------------- CLIENTS INPUTS HANDLING -------------------------------------
|
|
16593
|
-
// Adding propagation listener :
|
|
16594
|
-
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
// DBG
|
|
16598
|
-
lognow(">>>>>>>>FOR CLIENTS this.listeners",this.listeners);
|
|
16599
|
-
|
|
16600
|
-
}
|
|
16601
|
-
|
|
16602
|
-
// /*private*/getServersNodesIdsForModelObjectsForClient(clientObjectsIds){
|
|
16603
|
-
// const serversNodesIdsForModelObjectsForClient={};
|
|
16604
|
-
// const self=this;
|
|
16605
|
-
// // TODO : FIXME : INEFFICIENT !!!
|
|
16606
|
-
// foreach(clientObjectsIds,(objectId)=>{
|
|
16607
|
-
// foreach(self.modelObjectsDirectory,(objectsInNode, nodeId)=>{
|
|
16608
|
-
// if(contains(objectsInNode,objectId))
|
|
16609
|
-
// serversNodesIdsForModelObjectsForClient[objectId]=nodeId;
|
|
16610
|
-
// });
|
|
16611
|
-
// });
|
|
16612
|
-
//
|
|
16613
|
-
// return serversNodesIdsForModelObjectsForClient;
|
|
16614
|
-
// }
|
|
17060
|
+
lognow("ERROR : No clientSocket to client ! Cannot do reception treatment.");
|
|
17061
|
+
return;
|
|
17062
|
+
}
|
|
16615
17063
|
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
const server=self.server;
|
|
16619
|
-
|
|
16620
|
-
const theoreticNumberOfOutcomingServers=getArraySize(self.outcomingNodesOrigins);
|
|
16621
|
-
|
|
16622
|
-
return new Promise((resolve,error)=>{
|
|
16623
|
-
|
|
16624
|
-
// In case we timeout :
|
|
16625
|
-
setTimeout(()=>{
|
|
16626
|
-
if(self.isConnectedToCluster) return;
|
|
16627
|
-
self.isConnectedToCluster=true;
|
|
16628
|
-
resolve(server);
|
|
16629
|
-
},AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT);
|
|
16630
|
-
|
|
16631
|
-
// We try to connect to all the other outcoming servers :
|
|
16632
|
-
|
|
16633
|
-
// Special case : if node has no outcoming serves, (it's the seed node), then
|
|
16634
|
-
// it should be considered as connected to cluster nonetheless :
|
|
16635
|
-
if(empty(self.outcomingNodesOrigins)){
|
|
16636
|
-
self.finalizeClusterConnection(self.nodeId);
|
|
16637
|
-
resolve(server);
|
|
16638
|
-
return;
|
|
16639
|
-
}
|
|
16640
|
-
|
|
16641
|
-
// We try to connect to all outcoming nodes :
|
|
16642
|
-
foreach(self.outcomingNodesOrigins, outcomingNodeOrigin=>{
|
|
16643
|
-
|
|
16644
|
-
// TRACE
|
|
16645
|
-
lognow(` (${self.nodeId}) Sending registering response to node ${outcomingNodeOrigin}...`);
|
|
16646
|
-
|
|
16647
|
-
const splits=splitURL(outcomingNodeOrigin);
|
|
16648
|
-
const outcomingNodeProtocol=nonull(splits.protocol,"ws");
|
|
16649
|
-
const outcomingNodeHost=nonull(splits.host,"localhost");
|
|
16650
|
-
const outcomingNodePort=nonull(splits.port,"40000");
|
|
16651
|
-
//const isSecure=splits.isSecure; // UNUSED
|
|
16652
|
-
|
|
16653
|
-
const clientInstance=initClient(true, false, (socketToServer)=>{
|
|
16654
|
-
|
|
16655
|
-
// TODO : FIXME : IF CONNECTION TO SERVER FAILS, IT MUST BE ABLE TO TRY AGAIN LATER !
|
|
16656
|
-
|
|
16657
|
-
// TRACE
|
|
16658
|
-
lognow(` (${self.nodeId}) Sending registering request to outcoming node...`);
|
|
16659
|
-
|
|
16660
|
-
const helloRequest={
|
|
16661
|
-
nodeId:self.nodeId,
|
|
16662
|
-
type:"request.register.server.incoming"
|
|
16663
|
-
};
|
|
16664
|
-
socketToServer.send("protocol", helloRequest);
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
// We place a listener from outcoming node on this client instance to this outcoming node :
|
|
16668
|
-
socketToServer.receive("protocol",(message)=>{
|
|
16669
|
-
if(message.type==="response.register.server.incoming"){
|
|
16670
|
-
|
|
16671
|
-
// TRACE
|
|
16672
|
-
lognow(` (${self.nodeId}) Receving registering response from requested outcoming node...`);
|
|
16673
|
-
|
|
16674
|
-
const welcomeNodeId=message.nodeId;
|
|
16675
|
-
const duplicatesFreeAuthorizedNodesIds=[...new Set([...self.authorizedNodesIds, ...message.authorizedNodesIds, welcomeNodeId ])];
|
|
16676
|
-
self.authorizedNodesIds=duplicatesFreeAuthorizedNodesIds;
|
|
16677
|
-
|
|
16678
|
-
self.outcomingServers[welcomeNodeId]={clientInstance:clientInstance};
|
|
16679
|
-
|
|
16680
|
-
// Once we have registered to all the outcoming nodes :
|
|
16681
|
-
const currentNumberOfOutcomingServers=getArraySize(self.outcomingServers);
|
|
16682
|
-
if(!self.isConnectedToCluster && theoreticNumberOfOutcomingServers<=currentNumberOfOutcomingServers){
|
|
16683
|
-
//self.finalizeClusterConnection(welcomeNodeId);
|
|
16684
|
-
self.finalizeClusterConnection(self.nodeId);
|
|
16685
|
-
resolve(server);
|
|
16686
|
-
}
|
|
16687
|
-
}
|
|
16688
|
-
});
|
|
16689
|
-
|
|
16690
|
-
// Listeners handling ;
|
|
16691
|
-
foreach(Object.keys(self.listeners), channelName=>{
|
|
16692
|
-
socketToServer.receive(channelName, (message, clientSocket)=>{
|
|
16693
|
-
|
|
16694
|
-
// OUTCOMING NODES LISTENERS HOOK :
|
|
16695
|
-
// TRACE
|
|
16696
|
-
console.log("INFO : SERVER : Outcoming node has sent a "+channelName+" message: ", message);
|
|
16697
|
-
foreach(self.listeners[channelName], listener => {
|
|
16698
|
-
listener.execute(self, message, self.server, clientSocket);
|
|
16699
|
-
},(listener)=>(message.type===listener.messageType));
|
|
16700
|
-
|
|
16701
|
-
});
|
|
16702
|
-
});
|
|
16703
|
-
|
|
16704
|
-
|
|
16705
|
-
}, outcomingNodeProtocol+"://"+outcomingNodeHost, outcomingNodePort, false);
|
|
16706
|
-
clientInstance.client.start();
|
|
16707
|
-
|
|
16708
|
-
});
|
|
16709
|
-
|
|
16710
|
-
});
|
|
16711
|
-
|
|
16712
|
-
}
|
|
16713
|
-
|
|
16714
|
-
/*private*/finalizeClusterConnection(welcomeNodeId){
|
|
16715
|
-
// TRACE
|
|
16716
|
-
lognow(` (${this.nodeId}) Propagating this new node to the whole cluster...`);
|
|
17064
|
+
const dataWrapped=WebsocketImplementation.getMessageDataBothImplementations(eventOrMessage);
|
|
16717
17065
|
|
|
16718
|
-
// We
|
|
16719
|
-
const
|
|
16720
|
-
|
|
16721
|
-
|
|
16722
|
-
}
|
|
16723
|
-
this.propagateToCluster(newNodeRequest);
|
|
17066
|
+
// We check if the message is in the right channel (channel information is stored in exchanged wrapped data) :
|
|
17067
|
+
const channelName=this.channelName;
|
|
17068
|
+
if(dataWrapped.channelName && dataWrapped.channelName!==this.channelName){
|
|
17069
|
+
return;
|
|
17070
|
+
}
|
|
16724
17071
|
|
|
16725
|
-
|
|
17072
|
+
// We check if the message is in the right room (r)oom information is stored in client socket object) :
|
|
17073
|
+
if(!WebsocketImplementation.isInRoom(clientSocket, this.clientsRoomsTag)) return;
|
|
16726
17074
|
|
|
16727
|
-
this.addPropagationListeners();
|
|
16728
|
-
this.addPropagationForClientsListeners();
|
|
16729
|
-
}
|
|
16730
|
-
|
|
16731
|
-
|
|
16732
17075
|
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
// The node asks the cluster for its model partition :
|
|
17076
|
+
// We check if the message matches the required message type :
|
|
17077
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType && dataWrapped.type
|
|
17078
|
+
&& this.listenerConfig.listenerMessageType!==dataWrapped.type){
|
|
17079
|
+
return;
|
|
17080
|
+
}
|
|
16740
17081
|
|
|
16741
|
-
//
|
|
17082
|
+
// We check if the message matches the condition :
|
|
17083
|
+
const dataLocal=dataWrapped.data;
|
|
17084
|
+
if(this.listenerConfig.condition && !this.istenerConfig.condition(dataLocal, clientSocket)){
|
|
17085
|
+
return;
|
|
17086
|
+
}
|
|
16742
17087
|
|
|
16743
|
-
//
|
|
16744
|
-
|
|
17088
|
+
// We check if we have the same message id:
|
|
17089
|
+
const messageIdLocal=this.listenerConfig.messageId;
|
|
17090
|
+
if(messageIdLocal!==dataLocal.messageId){
|
|
17091
|
+
return;
|
|
17092
|
+
}
|
|
17093
|
+
|
|
17094
|
+
// At this point, message is the right one, so it can be processed :
|
|
17095
|
+
// --------------------------------
|
|
16745
17096
|
|
|
16746
|
-
//
|
|
17097
|
+
// We remove one-time usage listeners :
|
|
17098
|
+
// We remove BEFORE executing doOnIncomingMessage(), because in this reception entry point function we can have other listener registration UNDER THE SAME ID !
|
|
17099
|
+
if(this.listenerConfig && this.listenerConfig.destroyListenerAfterReceiving)
|
|
17100
|
+
remove(clientReceptionEntryPoints, this);
|
|
16747
17101
|
|
|
16748
|
-
|
|
16749
|
-
|
|
16750
|
-
type:"request.model.getSize",
|
|
16751
|
-
};
|
|
16752
|
-
this.propagateToCluster(modelSizeRequest);
|
|
17102
|
+
if(this.doOnIncomingMessage)
|
|
17103
|
+
this.doOnIncomingMessage(dataLocal, clientSocket);
|
|
16753
17104
|
|
|
16754
17105
|
}
|
|
16755
17106
|
|
|
16756
|
-
|
|
16757
|
-
/*private*/addPropagationListeners(){
|
|
16758
|
-
|
|
16759
|
-
// Adding propagation listener for acknowledging new node added to cluster :
|
|
16760
|
-
this.setPropagation("cluster","request.node.new", (self, message, server, clientSocket)=>{
|
|
16761
|
-
const newNodeId=message.nodeId;
|
|
16762
|
-
// TRACE
|
|
16763
|
-
lognow(` (${self.nodeId}) Acknowledging new node ${newNodeId} added to cluster...`);
|
|
16764
|
-
if(!contains(self.authorizedNodesIds,newNodeId)) self.authorizedNodesIds.push(newNodeId);
|
|
16765
|
-
});
|
|
16766
|
-
|
|
16767
|
-
// Adding propagation listener if a node receives a model size request :
|
|
16768
|
-
this.setPropagation("cluster","request.model.getSize",(self, message, server, clientSocket)=>{
|
|
16769
|
-
const modelSize=self.model.getModelSize();
|
|
16770
|
-
// TRACE
|
|
16771
|
-
lognow(`(${self.nodeId}) Node gives its model size to node ${message.nodeId}, modelSize=${modelSize}...`);
|
|
16772
|
-
return modelSize;
|
|
16773
|
-
},"response.model.getSize");
|
|
16774
|
-
|
|
16775
|
-
// Adding propagation listener :
|
|
16776
|
-
this.modelSizes={};
|
|
16777
|
-
this.setPropagation("cluster","response.model.getSize",(self, message, server, clientSocket)=>{
|
|
16778
|
-
|
|
16779
|
-
const modelSizes=self.modelSizes;
|
|
16780
|
-
const currentClusterSize=getArraySize(self.authorizedNodesIds);
|
|
16781
|
-
|
|
16782
|
-
const concernedNodeId=message.nodeId;
|
|
16783
|
-
const modelSize=message.result;
|
|
16784
|
-
|
|
16785
|
-
let currentNumberOfAnswers=getArraySize(modelSizes);
|
|
16786
|
-
|
|
16787
|
-
if(currentNumberOfAnswers<=currentClusterSize-1){// -1 because we want to exclude this node also !
|
|
16788
|
-
modelSizes[concernedNodeId]=modelSize;
|
|
16789
|
-
}
|
|
17107
|
+
}
|
|
16790
17108
|
|
|
16791
|
-
currentNumberOfAnswers=getArraySize(modelSizes);
|
|
16792
|
-
// TRACE
|
|
16793
|
-
lognow(`(${self.nodeId}) Node receiving a model size from node ${message.nodeId}...currentClusterSize=${currentClusterSize} ; modelSizes:`,modelSizes);
|
|
16794
|
-
if(currentClusterSize-1<=currentNumberOfAnswers){
|
|
16795
|
-
const nodeIdWithBiggestModel=Math.maxInArray(modelSizes, true);
|
|
16796
|
-
// TRACE
|
|
16797
|
-
lognow(`(${self.nodeId}) Node with biggest model is nodeIdWithBiggestModel=${nodeIdWithBiggestModel}...`);
|
|
16798
|
-
|
|
16799
|
-
const modelPartitionRequest={
|
|
16800
|
-
type:"request.model.partition",
|
|
16801
|
-
};
|
|
16802
|
-
this.propagateToCluster(modelPartitionRequest, nodeIdWithBiggestModel);
|
|
16803
|
-
}
|
|
16804
|
-
});
|
|
16805
|
-
|
|
16806
|
-
// Adding propagation listener :
|
|
16807
|
-
// If a node receives a model partition request :
|
|
16808
|
-
this.setPropagation("cluster","request.model.partition",(self, message, server, clientSocket)=>{
|
|
16809
|
-
// Each node will give a little portion of its model, according to its size :
|
|
16810
|
-
// TRACE
|
|
16811
|
-
lognow(`(${self.nodeId}) Node splits its model for node ${message.nodeId}...`);
|
|
16812
|
-
|
|
16813
|
-
const ownModel=self.model;
|
|
16814
|
-
const splittedModel=ownModel.split();
|
|
16815
|
-
|
|
16816
|
-
// Node shares also its model objects directory with the newcomer node :
|
|
16817
|
-
const ownModelObjectsIds=ownModel.getAllObjectsIds();
|
|
16818
|
-
self.modelObjectsDirectory[self.nodeId]=ownModelObjectsIds;
|
|
16819
|
-
|
|
16820
|
-
const modelString=JSON.stringifyDecycle(splittedModel);
|
|
16821
|
-
const modelPartitionResponse={
|
|
16822
|
-
type:"response.model.partition",
|
|
16823
|
-
modelString:modelString,
|
|
16824
|
-
modelObjectsDirectory:self.modelObjectsDirectory
|
|
16825
|
-
};
|
|
16826
|
-
this.propagateToCluster(modelPartitionResponse, message.nodeId);
|
|
16827
|
-
|
|
16828
|
-
// This node also advertises that its model has changed to all other nodes (not the newcomer, because it's unnecessary) :
|
|
16829
|
-
const updatemodelObjectsDirectoryRequest={
|
|
16830
|
-
type:"request.update.modelObjectsDirectory",
|
|
16831
|
-
ownModelObjectsIds:ownModelObjectsIds
|
|
16832
|
-
};
|
|
16833
|
-
this.propagateToCluster(updatemodelObjectsDirectoryRequest, null, [message.nodeId]);
|
|
16834
|
-
|
|
16835
|
-
});
|
|
16836
17109
|
|
|
16837
|
-
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
|
|
16841
|
-
|
|
16842
|
-
const newModel=JSON.parseRecycle(message.modelString);
|
|
16843
|
-
self.model.replaceBy(newModel);
|
|
16844
|
-
|
|
16845
|
-
// We initialize this node's model objects directory from the one froom the node tha provided it its model partition :
|
|
16846
|
-
self.modelObjectsDirectory=message.modelObjectsDirectory;
|
|
16847
|
-
|
|
16848
|
-
// Once this node has received its model partition, it updates its model objects directory
|
|
16849
|
-
const ownModelObjectsIds=newModel.getAllObjectsIds();
|
|
16850
|
-
self.modelObjectsDirectory[self.nodeId]=ownModelObjectsIds;
|
|
16851
|
-
|
|
16852
|
-
// TRACE
|
|
16853
|
-
lognow(`(${self.nodeId}) Node sends a model objects directory update request...ownModelObjectsIds=${ownModelObjectsIds}`);
|
|
16854
|
-
|
|
16855
|
-
// And notifies the other nodes of the change :
|
|
16856
|
-
const updatemodelObjectsDirectoryRequest={
|
|
16857
|
-
type:"request.update.modelObjectsDirectory",
|
|
16858
|
-
ownModelObjectsIds:ownModelObjectsIds
|
|
16859
|
-
};
|
|
16860
|
-
this.propagateToCluster(updatemodelObjectsDirectoryRequest);
|
|
17110
|
+
//
|
|
17111
|
+
// CLIENT INSTANCE :
|
|
17112
|
+
//
|
|
17113
|
+
class ClientInstance{
|
|
16861
17114
|
|
|
16862
|
-
|
|
16863
|
-
|
|
16864
|
-
this.addModelObjectsDirectoryListeners();
|
|
17115
|
+
constructor(clientSocket){
|
|
16865
17116
|
|
|
17117
|
+
this.onServerLostListeners=[];
|
|
16866
17118
|
|
|
16867
|
-
|
|
16868
|
-
|
|
17119
|
+
this.clientSocket=clientSocket;
|
|
17120
|
+
this.clientReceptionEntryPoints=[];
|
|
16869
17121
|
|
|
16870
17122
|
}
|
|
16871
17123
|
|
|
16872
17124
|
|
|
16873
|
-
|
|
16874
|
-
/*private*/addModelObjectsDirectoryListeners(){
|
|
16875
|
-
|
|
17125
|
+
sendChainable(channelNameParam, data, clientsRoomsTag=null){
|
|
16876
17126
|
const self=this;
|
|
16877
17127
|
|
|
16878
|
-
//
|
|
16879
|
-
|
|
16880
|
-
this.setPropagation("cluster","request.update.modelObjectsDirectory",(self, message, server, clientSocket)=>{
|
|
16881
|
-
|
|
16882
|
-
// TRACE
|
|
16883
|
-
lognow(`(${self.nodeId}) !!!!!! Node receives a model objects directory update request from node ${message.nodeId}...`,message);
|
|
16884
|
-
|
|
16885
|
-
const modelObjectsIds=message.ownModelObjectsIds;
|
|
16886
|
-
self.modelObjectsDirectory[message.nodeId]=modelObjectsIds;
|
|
16887
|
-
|
|
16888
|
-
});
|
|
17128
|
+
// DBG
|
|
17129
|
+
lognow(">>>>>>sendChainable CLIENT ("+channelNameParam+"):data:",data);
|
|
16889
17130
|
|
|
16890
|
-
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
16894
|
-
|
|
16895
|
-
|
|
16896
|
-
|
|
17131
|
+
// We add a message id :
|
|
17132
|
+
const messageId=getUUID();
|
|
17133
|
+
data.messageId=messageId;
|
|
17134
|
+
|
|
17135
|
+
// 1) We prepare the reception :
|
|
17136
|
+
const resultPromise={
|
|
17137
|
+
// CAUTION : CANNOT STORE ANYTHING IN THE resultPromise, FOR ASYNC CALLS MAKES ITS DATA INCONSISTENT IN TIME !
|
|
17138
|
+
thenWhenReceiveMessageType:(channelNameForResponse, listenerConfig={listenerMessageType:"",condition:()=>true}, doOnIncomingMessageForResponse)=>{
|
|
16897
17139
|
|
|
16898
|
-
|
|
16899
|
-
|
|
17140
|
+
listenerConfig=nonull(listenerConfig,{listenerMessageType:"",condition:()=>true});
|
|
17141
|
+
const listenerId=nonull(listenerConfig.listenerMessageType,"");
|
|
17142
|
+
|
|
17143
|
+
// I-
|
|
17144
|
+
//
|
|
17145
|
+
self.receive(channelNameForResponse, doOnIncomingMessageForResponse,
|
|
17146
|
+
clientsRoomsTag, listenerId, {messageId:messageId, destroyListenerAfterReceiving:true});
|
|
17147
|
+
//
|
|
17148
|
+
|
|
17149
|
+
return resultPromise;
|
|
17150
|
+
}
|
|
17151
|
+
};
|
|
16900
17152
|
|
|
16901
|
-
|
|
17153
|
+
// 2) We send the data :
|
|
17154
|
+
this.send(channelNameParam, data, clientsRoomsTag);
|
|
16902
17155
|
|
|
16903
|
-
}
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
/*public*/sendUpdatedObjects(modifiedObjects, modifiedObjectsIds){
|
|
16907
|
-
|
|
16908
|
-
if(empty(modifiedObjects)) return modifiedObjects;
|
|
16909
17156
|
|
|
16910
|
-
|
|
16911
|
-
return this.sendObjectsUpdatesToClients(modifiedObjects);
|
|
17157
|
+
return resultPromise;
|
|
16912
17158
|
}
|
|
16913
17159
|
|
|
16914
|
-
|
|
17160
|
+
// II-
|
|
17161
|
+
receive(channelNameParam, doOnIncomingMessage, clientsRoomsTag=null, entryPointId=null, listenerConfig={destroyListenerAfterReceiving:false}){
|
|
17162
|
+
const self=this;
|
|
16915
17163
|
|
|
16916
|
-
|
|
16917
|
-
|
|
16918
|
-
// At this point, node controller has already added the objects to its model.
|
|
16919
|
-
// We just want to update the objects directories of all the nodes in the cluster:
|
|
17164
|
+
// // DBG
|
|
17165
|
+
// lognow("INFO : (CLIENT) SETTING UP RECEIVE for :",channelNameParam);
|
|
16920
17166
|
|
|
16921
|
-
|
|
16922
|
-
|
|
16923
|
-
|
|
17167
|
+
const clientReceptionEntryPoint=new ClientReceptionEntryPoint(channelNameParam, entryPointId, clientsRoomsTag, listenerConfig, doOnIncomingMessage);
|
|
17168
|
+
if(!contains.filter((l)=>(
|
|
17169
|
+
l.entryPointId && clientReceptionEntryPoint.entryPointId
|
|
17170
|
+
&& l.entryPointId===clientReceptionEntryPoint.entryPointId
|
|
17171
|
+
))(this.clientReceptionEntryPoints)){
|
|
17172
|
+
|
|
17173
|
+
this.clientReceptionEntryPoints.push(clientReceptionEntryPoint);
|
|
17174
|
+
}
|
|
16924
17175
|
|
|
16925
|
-
|
|
16926
|
-
|
|
16927
|
-
|
|
16928
|
-
|
|
16929
|
-
|
|
16930
|
-
|
|
16931
|
-
|
|
16932
|
-
|
|
16933
|
-
|
|
16934
|
-
return this.sendObjectsUpdatesToClients(newObjects, true);
|
|
17176
|
+
const clientSocket=this.clientSocket;
|
|
17177
|
+
if(WebsocketImplementation.useSocketIOImplementation){
|
|
17178
|
+
// FOR THE SOCKETIO IMPLEMENTATION :
|
|
17179
|
+
clientSocket.on(channelNameParam, (eventOrMessage)=>{
|
|
17180
|
+
clientReceptionEntryPoint.execute(eventOrMessage, clientSocket, this.clientReceptionEntryPoints);
|
|
17181
|
+
});
|
|
17182
|
+
}
|
|
17183
|
+
|
|
17184
|
+
return this;
|
|
16935
17185
|
}
|
|
17186
|
+
|
|
16936
17187
|
|
|
16937
|
-
|
|
17188
|
+
send(channelNameParam, data, clientsRoomsTag=null){
|
|
16938
17189
|
|
|
16939
|
-
|
|
16940
|
-
|
|
16941
|
-
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
self.server.send("inputs", objectsModifiedOrAddedClientResponse, null, clientSocket);
|
|
16949
|
-
});
|
|
17190
|
+
// // DBG
|
|
17191
|
+
// lognow("(CLIENT) CLIENT TRIES TO SEND !");
|
|
17192
|
+
|
|
17193
|
+
const clientSocket=this.clientSocket;
|
|
17194
|
+
|
|
17195
|
+
if(!isConnected(clientSocket)) return;
|
|
17196
|
+
|
|
17197
|
+
// Room information is stored in client socket object :
|
|
17198
|
+
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
16950
17199
|
|
|
16951
|
-
|
|
17200
|
+
// Channel information is stored in exchanged data :
|
|
17201
|
+
let dataWrapped={channelName:channelNameParam, data:data};
|
|
17202
|
+
|
|
17203
|
+
dataWrapped=stringifyObject(dataWrapped);
|
|
17204
|
+
|
|
17205
|
+
// TODO : FIXME : Use one single interface !
|
|
17206
|
+
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
17207
|
+
else clientSocket.emit(channelNameParam,dataWrapped);
|
|
17208
|
+
|
|
17209
|
+
return this;
|
|
16952
17210
|
}
|
|
16953
17211
|
|
|
16954
17212
|
|
|
16955
|
-
|
|
16956
|
-
|
|
16957
|
-
|
|
16958
|
-
|
|
16959
|
-
foreach(this.incomingServers,(incomingServer,nodeId)=>{
|
|
16960
|
-
neighborsNodesIds.push(nodeId);
|
|
16961
|
-
});
|
|
16962
|
-
foreach(this.outcomingServers,(outcomingServer,nodeId)=>{
|
|
16963
|
-
neighborsNodesIds.push(nodeId);
|
|
16964
|
-
});
|
|
16965
|
-
return neighborsNodesIds;
|
|
17213
|
+
join(clientRoomTag){
|
|
17214
|
+
// Join room client part protocol :
|
|
17215
|
+
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
17216
|
+
this.send("protocol",message);
|
|
16966
17217
|
}
|
|
17218
|
+
|
|
16967
17219
|
|
|
16968
|
-
|
|
17220
|
+
onConnectionToServer(doOnConnection){
|
|
16969
17221
|
const self=this;
|
|
16970
|
-
|
|
16971
|
-
// We send the new objects to the neighbor node with the least amount of objects :
|
|
16972
|
-
let leastOccupiedNodeId=null;
|
|
16973
|
-
foreach(this.getNeighborsNodesIds(), (nodeId)=>{
|
|
16974
|
-
const entry=self.modelObjectsDirectory[nodeId];
|
|
16975
|
-
const numberOfObjects=getArraySize(entry);
|
|
16976
|
-
if(!leastOccupiedNodeId || numberOfObjects<getArraySize(self.modelObjectsDirectory[leastOccupiedNodeId]))
|
|
16977
|
-
leastOccupiedNodeId=nodeId;
|
|
16978
|
-
});
|
|
16979
17222
|
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
return leastOccupiedNodeId;
|
|
16985
|
-
}
|
|
16986
|
-
|
|
17223
|
+
// DBG
|
|
17224
|
+
lognow("DEBUG : CLIENT : setting up onConnectionToServer.");
|
|
17225
|
+
|
|
17226
|
+
const doAllOnConnection=()=>{
|
|
16987
17227
|
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
this.setUniqueReceptionPoint(channelName, requestType, (self, message, server, clientSocket)=>{
|
|
17228
|
+
// To avoid triggering this event several times, depending on the implementation :
|
|
17229
|
+
if(self.hasConnectEventFired) return;
|
|
17230
|
+
self.hasConnectEventFired=true;
|
|
16992
17231
|
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
// TRACE
|
|
16996
|
-
lognow(`WARN : Request of type ${message.type} already answered. Aborting.`);
|
|
16997
|
-
return;
|
|
16998
|
-
}
|
|
16999
|
-
if(message.visitedNodeIds){
|
|
17000
|
-
if(contains(message.visitedNodeIds, self.nodeId)){
|
|
17001
|
-
return;
|
|
17002
|
-
}
|
|
17003
|
-
message.visitedNodeIds.push(self.nodeId);
|
|
17004
|
-
}
|
|
17232
|
+
// DBG
|
|
17233
|
+
lognow("DEBUG : CLIENT : doOnConnection !");
|
|
17005
17234
|
|
|
17006
|
-
const
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
}else{
|
|
17015
|
-
if(message.destinationNodeId===self.nodeId && hasNoExclusionsOrExclusionDoesNotApplyOnThisNode){
|
|
17016
|
-
if(doOnReception)
|
|
17017
|
-
response=doOnReception(self, message, server, clientSocket);
|
|
17018
|
-
pushInArrayAsQueue(self.executedRequestIdsHistory, REQUESTS_IDS_HISTORY_SIZE, requestId);
|
|
17019
|
-
}else{
|
|
17020
|
-
if(propagateToAllCluster) self.sendToOtherNodes(channelName, message);
|
|
17021
|
-
}
|
|
17235
|
+
const clientSocket=self.clientSocket;
|
|
17236
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
17237
|
+
// FOR THE WEBSOCKET IMPLEMENTATION :
|
|
17238
|
+
clientSocket.addEventListener("message", (eventOrMessage)=>{
|
|
17239
|
+
foreach(self.clientReceptionEntryPoints,(clientReceptionEntryPoint)=>{
|
|
17240
|
+
clientReceptionEntryPoint.execute(eventOrMessage, clientSocket, self.clientReceptionEntryPoints);
|
|
17241
|
+
});
|
|
17242
|
+
});
|
|
17022
17243
|
}
|
|
17023
|
-
|
|
17024
|
-
// We send back the answer to the originating node :
|
|
17025
|
-
if(response!=null && responseRequestType){
|
|
17026
|
-
// TRACE
|
|
17027
|
-
lognow(`(${self.nodeId}) Node sending back a response of type ${responseRequestType}...`);
|
|
17028
17244
|
|
|
17029
|
-
|
|
17030
|
-
|
|
17031
|
-
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17245
|
+
doOnConnection(self, clientSocket);
|
|
17246
|
+
};
|
|
17247
|
+
|
|
17248
|
+
|
|
17249
|
+
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
17250
|
+
// FOR THE WEBSOCKET IMPLEMENTATION :
|
|
17251
|
+
this.clientSocket.addEventListener("open",doAllOnConnection);
|
|
17252
|
+
|
|
17253
|
+
if(!empty(this.onServerLostListeners)){
|
|
17254
|
+
this.clientSocket.addEventListener("close",()=>{
|
|
17255
|
+
foreach(self.onServerLostListeners,l=>{l.execute(self.clientSocket);});
|
|
17256
|
+
});
|
|
17036
17257
|
}
|
|
17037
17258
|
|
|
17038
|
-
}
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
/*private*/propagateToCluster(request, destinationNodeId=null, excludedNodesIds=null){
|
|
17043
|
-
request.requestId=getUUID();
|
|
17044
|
-
const originNodeId=this.nodeId;
|
|
17045
|
-
request.nodeId=originNodeId;
|
|
17046
|
-
if(destinationNodeId) request.destinationNodeId=destinationNodeId;
|
|
17047
|
-
if(excludedNodesIds) request.excludedNodesIds=excludedNodesIds;
|
|
17048
|
-
request.visitedNodeIds=[originNodeId];
|
|
17049
|
-
this.sendToOtherNodes("cluster",request);
|
|
17050
|
-
}
|
|
17051
|
-
|
|
17052
|
-
/*private*/propagateToNeighbors(request){
|
|
17053
|
-
request.requestId=getUUID();
|
|
17054
|
-
const originNodeId=this.nodeId;
|
|
17055
|
-
request.nodeId=originNodeId;
|
|
17056
|
-
this.sendToOtherNodes("neightbors", request);
|
|
17057
|
-
}
|
|
17058
|
-
/*private*/propagateToSingleNeighbor(request, destinationNodeId){
|
|
17059
|
-
request.requestId=getUUID();
|
|
17060
|
-
const originNodeId=this.nodeId;
|
|
17061
|
-
request.nodeId=originNodeId;
|
|
17062
|
-
request.destinationNodeId=destinationNodeId;
|
|
17063
|
-
this.sendToOtherSingleNode("neightbors", request, destinationNodeId);
|
|
17064
|
-
}
|
|
17065
|
-
|
|
17066
|
-
// ****************************************************************************************************
|
|
17067
|
-
|
|
17068
|
-
/*private*/sendToOtherNodes(channelName, message){
|
|
17069
|
-
let hasBeenSentIncoming=this.sendToIncomingServers(channelName, message);
|
|
17070
|
-
let hasBeenSentOutcoming=this.sendToOutcomingServers(channelName, message);
|
|
17071
|
-
return (hasBeenSentIncoming || hasBeenSentOutcoming);
|
|
17072
|
-
}
|
|
17073
|
-
/*private*/sendToIncomingServers(channelName, message){
|
|
17074
|
-
const self=this;
|
|
17075
|
-
let hasBeenSent=false;
|
|
17076
|
-
foreach(this.incomingServers, (client)=>{
|
|
17077
|
-
const clientSocket=client.clientSocket;
|
|
17078
|
-
self.server.send(channelName, message, null, clientSocket);
|
|
17079
|
-
hasBeenSent=true;
|
|
17080
|
-
});
|
|
17081
|
-
return hasBeenSent;
|
|
17082
|
-
}
|
|
17083
|
-
/*private*/sendToOutcomingServers(channelName, message){
|
|
17084
|
-
let hasBeenSent=false;
|
|
17085
|
-
foreach(this.outcomingServers, (client)=>{
|
|
17086
|
-
const clientInstanceToOtherServer=client.clientInstance;
|
|
17087
|
-
clientInstanceToOtherServer.client.socketToServer.send(channelName, message);
|
|
17088
|
-
hasBeenSent=true;
|
|
17089
|
-
});
|
|
17090
|
-
return hasBeenSent;
|
|
17091
|
-
}
|
|
17092
|
-
/*private*/sendToOtherSingleNode(channelName, request, destinationNodeId){
|
|
17093
|
-
const self=this;
|
|
17094
|
-
let hasBeenSent=false;
|
|
17095
|
-
foreach(this.incomingServers, (client)=>{
|
|
17096
|
-
const clientSocket=client.clientSocket;
|
|
17097
|
-
self.server.send(channelName, message, null, clientSocket);
|
|
17098
|
-
hasBeenSent=true;
|
|
17099
|
-
},(c, nodeId)=>(nodeId===destinationNodeId));
|
|
17100
|
-
if(!hasBeenSent){
|
|
17101
|
-
foreach(this.outcomingServers, (client)=>{
|
|
17102
|
-
const clientInstanceToOtherServer=client.clientInstance;
|
|
17103
|
-
clientInstanceToOtherServer.client.socketToServer.send(channelName, message);
|
|
17104
|
-
hasBeenSent=true;
|
|
17105
|
-
},(c, nodeId)=>(nodeId===destinationNodeId));
|
|
17106
|
-
}
|
|
17107
|
-
return hasBeenSent;
|
|
17108
|
-
}
|
|
17259
|
+
}else{
|
|
17260
|
+
// FOR THE SOCKETIO IMPLEMENTATION :
|
|
17261
|
+
this.clientSocket.on("connect",doAllOnConnection);
|
|
17109
17262
|
|
|
17110
|
-
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
if(listener.messageType===messageType) return true;
|
|
17115
|
-
})) return;
|
|
17116
|
-
listeners.push({
|
|
17117
|
-
messageType:messageType,
|
|
17118
|
-
execute:(self, message, server, clientSocket)=>{
|
|
17119
|
-
doOnReception(self, message, server, clientSocket);
|
|
17263
|
+
if(!empty(this.onServerLostListeners)){
|
|
17264
|
+
this.clientSocket.on("close",()=>{
|
|
17265
|
+
foreach(self.onServerLostListeners,l=>{l.execute(self.clientSocket);});
|
|
17266
|
+
});
|
|
17120
17267
|
}
|
|
17121
|
-
});
|
|
17122
|
-
}
|
|
17123
|
-
|
|
17124
|
-
// ****************************************************************************************************
|
|
17125
17268
|
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17269
|
+
|
|
17270
|
+
// Client ping handling :
|
|
17271
|
+
// (SocketIO implementation only, the websocket implementation sends back a hidden pong !)
|
|
17272
|
+
// PING-PONG :
|
|
17273
|
+
this.receive("protocol",(message)=>{
|
|
17274
|
+
if(message.type!=="ping") return;
|
|
17275
|
+
self.send("protocol",{type:"pong"});
|
|
17276
|
+
// // DBG
|
|
17277
|
+
// lognow("DEBUG : CLIENT : Pong sent.");
|
|
17278
|
+
});
|
|
17279
|
+
}
|
|
17280
|
+
|
|
17281
|
+
|
|
17282
|
+
|
|
17135
17283
|
}
|
|
17136
17284
|
|
|
17137
|
-
|
|
17138
|
-
|
|
17139
17285
|
}
|
|
17140
17286
|
|
|
17141
17287
|
|
|
17142
|
-
getAORTACNode=function(nodeId=getUUID(), selfOrigin="ws://127.0.0.1:40000", outcomingNodesOrigins=[], model, controller){
|
|
17143
|
-
return new AORTACNode(nodeId, selfOrigin, outcomingNodesOrigins, model, controller);
|
|
17144
|
-
}
|
|
17145
|
-
|
|
17146
17288
|
|
|
17147
17289
|
|
|
17148
17290
|
|
|
@@ -17150,18 +17292,32 @@ getAORTACNode=function(nodeId=getUUID(), selfOrigin="ws://127.0.0.1:40000", outc
|
|
|
17150
17292
|
|
|
17151
17293
|
class ServerReceptionEntryPoint{
|
|
17152
17294
|
|
|
17153
|
-
constructor(channelNameParam, clientsRoomsTag, doOnIncomingMessage){
|
|
17295
|
+
constructor(channelNameParam, clientsRoomsTag, listenerConfig=null, doOnIncomingMessage){
|
|
17154
17296
|
this.channelName=channelNameParam;
|
|
17155
17297
|
this.clientsRoomsTag=clientsRoomsTag;
|
|
17298
|
+
this.listenerConfig=listenerConfig;
|
|
17156
17299
|
this.doOnIncomingMessage=doOnIncomingMessage;
|
|
17157
17300
|
}
|
|
17158
17301
|
|
|
17159
17302
|
execute(eventOrMessage, clientSocketParam){
|
|
17160
17303
|
|
|
17304
|
+
if(!eventOrMessage){
|
|
17305
|
+
// TRACE
|
|
17306
|
+
lognow("ERROR : No message received.");
|
|
17307
|
+
return;
|
|
17308
|
+
}
|
|
17309
|
+
|
|
17310
|
+
if(!clientSocketParam){
|
|
17311
|
+
// TRACE
|
|
17312
|
+
lognow("ERROR : No clientSocket to client ! Cannot do reception treatment.");
|
|
17313
|
+
return;
|
|
17314
|
+
}
|
|
17315
|
+
|
|
17316
|
+
|
|
17161
17317
|
// With «ws» library we have no choive than register message events inside a «connection» event !
|
|
17162
17318
|
|
|
17163
17319
|
const dataWrapped=WebsocketImplementation.getMessageDataBothImplementations(eventOrMessage);
|
|
17164
|
-
|
|
17320
|
+
|
|
17165
17321
|
// Channel information is stored in exchanged data :
|
|
17166
17322
|
if(dataWrapped.channelName!==this.channelName) return;
|
|
17167
17323
|
|
|
@@ -17169,14 +17325,21 @@ class ServerReceptionEntryPoint{
|
|
|
17169
17325
|
// Room information is stored in client socket object :
|
|
17170
17326
|
const isClientInRoom=WebsocketImplementation.isInRoom(clientSocketParam, this.clientsRoomsTag);
|
|
17171
17327
|
|
|
17172
|
-
|
|
17173
|
-
|
|
17328
|
+
// // DBG
|
|
17329
|
+
// lognow("(SERVER) isClientInRoom:",isClientInRoom);
|
|
17174
17330
|
|
|
17175
17331
|
if(!isClientInRoom) return;
|
|
17332
|
+
|
|
17333
|
+
// We check if the message matches the required message type :
|
|
17334
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType && dataWrapped.type
|
|
17335
|
+
&& this.listenerConfig.listenerMessageType!==dataWrapped.type){
|
|
17336
|
+
return;
|
|
17337
|
+
}
|
|
17176
17338
|
|
|
17177
17339
|
if(this.doOnIncomingMessage){
|
|
17178
|
-
|
|
17179
|
-
|
|
17340
|
+
// // DBG
|
|
17341
|
+
// lognow("(SERVER) this.doOnIncomingMessage:");
|
|
17342
|
+
|
|
17180
17343
|
this.doOnIncomingMessage(dataWrapped.data, clientSocketParam);
|
|
17181
17344
|
}
|
|
17182
17345
|
|
|
@@ -17197,7 +17360,9 @@ class NodeServerInstance{
|
|
|
17197
17360
|
this.onClientLostListeners=[];
|
|
17198
17361
|
|
|
17199
17362
|
this.clientPingIntervalMillis=5000;
|
|
17363
|
+
|
|
17200
17364
|
this.serverSocket=serverSocket;
|
|
17365
|
+
|
|
17201
17366
|
this.listenableServer=listenableServer;
|
|
17202
17367
|
this.serverReceptionEntryPoints=[];
|
|
17203
17368
|
|
|
@@ -17230,13 +17395,12 @@ class NodeServerInstance{
|
|
|
17230
17395
|
//}
|
|
17231
17396
|
|
|
17232
17397
|
|
|
17233
|
-
receive(channelNameParam, doOnIncomingMessage, clientsRoomsTag=null){
|
|
17234
|
-
const self=this;
|
|
17398
|
+
receive(channelNameParam, doOnIncomingMessage, listenerConfig=null, clientsRoomsTag=null){
|
|
17235
17399
|
|
|
17236
17400
|
// DBG
|
|
17237
17401
|
lognow("(SERVER) Registering receive for «"+channelNameParam+"»...");
|
|
17238
17402
|
|
|
17239
|
-
const serverReceptionEntryPoint=new ServerReceptionEntryPoint(channelNameParam, clientsRoomsTag, doOnIncomingMessage);
|
|
17403
|
+
const serverReceptionEntryPoint=new ServerReceptionEntryPoint(channelNameParam, clientsRoomsTag, listenerConfig, doOnIncomingMessage);
|
|
17240
17404
|
this.serverReceptionEntryPoints.push(serverReceptionEntryPoint);
|
|
17241
17405
|
|
|
17242
17406
|
// SPECIAL FOR THE SOCKETIO IMPLEMENTATION :
|
|
@@ -17445,255 +17609,13 @@ class NodeServerInstance{
|
|
|
17445
17609
|
|
|
17446
17610
|
|
|
17447
17611
|
|
|
17448
|
-
/* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.
|
|
17449
|
-
*
|
|
17450
|
-
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
17451
|
-
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
17452
|
-
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
17453
|
-
*
|
|
17454
|
-
* # Library name : «aotrautils»
|
|
17455
|
-
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
17456
|
-
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
17457
|
-
* # Author email : info@alqemia.com
|
|
17458
|
-
* # Organization name : Alqemia
|
|
17459
|
-
* # Organization email : admin@alqemia.com
|
|
17460
|
-
* # Organization website : https://alqemia.com
|
|
17461
|
-
*
|
|
17462
|
-
*
|
|
17463
|
-
*/
|
|
17464
|
-
|
|
17465
|
-
|
|
17466
|
-
|
|
17467
|
-
// COMPATIBILITY browser javascript / nodejs environment :
|
|
17468
|
-
if(typeof(window)==="undefined") window=global;
|
|
17469
|
-
|
|
17470
|
-
|
|
17471
|
-
|
|
17472
|
-
// ==================================================================================================================
|
|
17473
|
-
|
|
17474
|
-
|
|
17475
|
-
class ClientReceptionEntryPoint{
|
|
17476
|
-
|
|
17477
|
-
constructor(channelNameParam, entryPointId, clientsRoomsTag, listenerConfig, doOnIncomingMessage){
|
|
17478
|
-
this.channelName=channelNameParam;
|
|
17479
|
-
this.entryPointId=entryPointId;
|
|
17480
|
-
this.clientsRoomsTag=clientsRoomsTag;
|
|
17481
|
-
this.listenerConfig=listenerConfig;
|
|
17482
|
-
this.doOnIncomingMessage=doOnIncomingMessage;
|
|
17483
|
-
}
|
|
17484
|
-
|
|
17485
|
-
// III-
|
|
17486
|
-
execute(eventOrMessage, clientSocket, clientReceptionEntryPoints){
|
|
17487
|
-
|
|
17488
|
-
|
|
17489
|
-
const dataWrapped=WebsocketImplementation.getMessageDataBothImplementations(eventOrMessage);
|
|
17490
|
-
|
|
17491
|
-
// We check if the message is in the right channel (channel information is stored in exchanged wrapped data) :
|
|
17492
|
-
const channelName=this.channelName;
|
|
17493
|
-
if(dataWrapped.channelName && dataWrapped.channelName!==this.channelName){
|
|
17494
|
-
return;
|
|
17495
|
-
}
|
|
17496
|
-
|
|
17497
|
-
// We check if the message is in the right room (r)oom information is stored in client socket object) :
|
|
17498
|
-
if(!WebsocketImplementation.isInRoom(clientSocket, this.clientsRoomsTag)){
|
|
17499
|
-
return;
|
|
17500
|
-
}
|
|
17501
|
-
|
|
17502
|
-
// We check if the message matches the required message type :
|
|
17503
|
-
if( this.listenerConfig && this.listenerConfig.messageType && dataWrapped.type
|
|
17504
|
-
&& this.listenerConfig.messageType!==dataWrapped.type){
|
|
17505
|
-
return;
|
|
17506
|
-
}
|
|
17507
|
-
|
|
17508
|
-
// We check if the message matches the condition :
|
|
17509
|
-
const dataLocal=dataWrapped.data;
|
|
17510
|
-
if(this.listenerConfig.condition && !this.istenerConfig.condition(dataLocal, clientSocket)){
|
|
17511
|
-
return;
|
|
17512
|
-
}
|
|
17513
|
-
|
|
17514
|
-
// We check if we have the same message id:
|
|
17515
|
-
const messageIdLocal=this.listenerConfig.messageId;
|
|
17516
|
-
if(messageIdLocal!==dataLocal.messageId){
|
|
17517
|
-
return;
|
|
17518
|
-
}
|
|
17519
|
-
|
|
17520
|
-
// At this point, message is the right one, so it can be processed :
|
|
17521
|
-
// --------------------------------
|
|
17522
|
-
|
|
17523
|
-
// We remove one-time usage listeners :
|
|
17524
|
-
// We remove BEFORE executing doOnIncomingMessage(), because in this reception entry point function we can have other listener registration UNDER THE SAME ID !
|
|
17525
|
-
if(this.listenerConfig && this.listenerConfig.destroyListenerAfterReceiving)
|
|
17526
|
-
remove(clientReceptionEntryPoints, this);
|
|
17527
|
-
|
|
17528
|
-
if(this.doOnIncomingMessage)
|
|
17529
|
-
this.doOnIncomingMessage(dataLocal, clientSocket);
|
|
17530
|
-
|
|
17531
|
-
}
|
|
17532
|
-
|
|
17533
|
-
}
|
|
17534
|
-
|
|
17535
|
-
|
|
17536
|
-
//
|
|
17537
|
-
// CLIENT INSTANCE :
|
|
17538
|
-
//
|
|
17539
|
-
class ClientInstance{
|
|
17540
|
-
|
|
17541
|
-
constructor(clientSocket){
|
|
17542
|
-
this.clientSocket=clientSocket;
|
|
17543
|
-
this.clientReceptionEntryPoints=[];
|
|
17544
|
-
|
|
17545
|
-
}
|
|
17546
|
-
|
|
17547
|
-
|
|
17548
|
-
sendChainable(channelNameParam, data, clientsRoomsTag=null){
|
|
17549
|
-
const self=this;
|
|
17550
|
-
|
|
17551
|
-
// DBG
|
|
17552
|
-
lognow(">>>>>>sendChainable CLIENT ("+channelNameParam+"):data:",data);
|
|
17553
|
-
|
|
17554
|
-
// We add a message id :
|
|
17555
|
-
const messageId=getUUID();
|
|
17556
|
-
data.messageId=messageId;
|
|
17557
|
-
|
|
17558
|
-
// 1) We prepare the reception :
|
|
17559
|
-
const resultPromise={
|
|
17560
|
-
// CAUTION : CANNOT STORE ANYTHING IN THE resultPromise, FOR ASYNC CALLS MAKES ITS DATA INCONSISTENT IN TIME !
|
|
17561
|
-
thenWhenReceiveMessageType:(channelNameForResponse, listenerConfig={messageType:"",condition:()=>true}, doOnIncomingMessageForResponse)=>{
|
|
17562
|
-
|
|
17563
|
-
listenerConfig=nonull(listenerConfig,{messageType:"",condition:()=>true});
|
|
17564
|
-
const listenerId=nonull(listenerConfig.messageType,"");
|
|
17565
|
-
|
|
17566
|
-
// I-
|
|
17567
|
-
//
|
|
17568
|
-
self.receive(channelNameForResponse, doOnIncomingMessageForResponse
|
|
17569
|
-
, clientsRoomsTag, listenerId, {messageId:messageId, destroyListenerAfterReceiving:true});
|
|
17570
|
-
//
|
|
17571
|
-
|
|
17572
|
-
return resultPromise;
|
|
17573
|
-
}
|
|
17574
|
-
};
|
|
17575
|
-
|
|
17576
|
-
// 2) We send the data :
|
|
17577
|
-
this.send(channelNameParam, data, clientsRoomsTag);
|
|
17578
|
-
|
|
17579
17612
|
|
|
17580
|
-
return resultPromise;
|
|
17581
|
-
}
|
|
17582
|
-
|
|
17583
|
-
// II-
|
|
17584
|
-
receive(channelNameParam, doOnIncomingMessage, clientsRoomsTag=null, entryPointId=null, listenerConfig={destroyListenerAfterReceiving:false}){
|
|
17585
|
-
const self=this;
|
|
17586
|
-
|
|
17587
|
-
// DBG
|
|
17588
|
-
lognow("INFO : (CLIENT) SETTING UP RECEIVE for :",channelNameParam);
|
|
17589
|
-
|
|
17590
|
-
const clientReceptionEntryPoint=new ClientReceptionEntryPoint(channelNameParam, entryPointId, clientsRoomsTag, listenerConfig, doOnIncomingMessage);
|
|
17591
|
-
if(!contains.filter((l)=>(
|
|
17592
|
-
l.entryPointId && clientReceptionEntryPoint.entryPointId
|
|
17593
|
-
&& l.entryPointId===clientReceptionEntryPoint.entryPointId
|
|
17594
|
-
))(this.clientReceptionEntryPoints)){
|
|
17595
|
-
|
|
17596
|
-
this.clientReceptionEntryPoints.push(clientReceptionEntryPoint);
|
|
17597
|
-
}
|
|
17598
17613
|
|
|
17599
|
-
const clientSocket=this.clientSocket;
|
|
17600
|
-
if(WebsocketImplementation.useSocketIOImplementation){
|
|
17601
|
-
// FOR THE SOCKETIO IMPLEMENTATION :
|
|
17602
|
-
clientSocket.on(channelNameParam, (eventOrMessage)=>{
|
|
17603
|
-
clientReceptionEntryPoint.execute(eventOrMessage, clientSocket, this.clientReceptionEntryPoints);
|
|
17604
|
-
});
|
|
17605
|
-
}
|
|
17606
|
-
|
|
17607
|
-
return this;
|
|
17608
|
-
}
|
|
17609
17614
|
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
send(channelNameParam, data, clientsRoomsTag=null){
|
|
17614
|
-
|
|
17615
|
-
// // DBG
|
|
17616
|
-
// lognow("(CLIENT) CLIENT TRIES TO SEND !");
|
|
17617
|
-
|
|
17618
|
-
const clientSocket=this.clientSocket;
|
|
17619
|
-
|
|
17620
|
-
if(!isConnected(clientSocket)) return;
|
|
17621
|
-
|
|
17622
|
-
// Room information is stored in client socket object :
|
|
17623
|
-
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
17624
17615
|
|
|
17625
|
-
// Channel information is stored in exchanged data :
|
|
17626
|
-
let dataWrapped={channelName:channelNameParam, data:data};
|
|
17627
|
-
|
|
17628
|
-
dataWrapped=stringifyObject(dataWrapped);
|
|
17629
|
-
|
|
17630
|
-
// TODO : FIXME : Use one single interface !
|
|
17631
|
-
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
17632
|
-
else clientSocket.emit(channelNameParam,dataWrapped);
|
|
17633
|
-
|
|
17634
|
-
return this;
|
|
17635
|
-
}
|
|
17636
|
-
|
|
17637
|
-
|
|
17638
|
-
join(clientRoomTag){
|
|
17639
|
-
// Join room client part protocol :
|
|
17640
|
-
const message={type:"joinRoom",clientRoomTag:clientRoomTag};
|
|
17641
|
-
this.send("protocol",message);
|
|
17642
|
-
}
|
|
17643
|
-
|
|
17644
|
-
|
|
17645
|
-
onConnectionToServer(doOnConnection){
|
|
17646
|
-
const self=this;
|
|
17647
|
-
|
|
17648
|
-
// DBG
|
|
17649
|
-
lognow("DEBUG : CLIENT : setting up onConnectionToServer.");
|
|
17650
|
-
|
|
17651
|
-
const doAllOnConnection=()=>{
|
|
17652
17616
|
|
|
17653
|
-
// To avoid triggering this event several times, depending on the implementation :
|
|
17654
|
-
if(self.hasConnectEventFired) return;
|
|
17655
|
-
self.hasConnectEventFired=true;
|
|
17656
|
-
|
|
17657
|
-
// DBG
|
|
17658
|
-
lognow("DEBUG : CLIENT : doOnConnection !");
|
|
17659
|
-
|
|
17660
|
-
const clientSocket=self.clientSocket;
|
|
17661
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
17662
|
-
// FOR THE WEBSOCKET IMPLEMENTATION :
|
|
17663
|
-
clientSocket.addEventListener("message", (eventOrMessage)=>{
|
|
17664
|
-
foreach(self.clientReceptionEntryPoints,(clientReceptionEntryPoint)=>{
|
|
17665
|
-
clientReceptionEntryPoint.execute(eventOrMessage, clientSocket, self.clientReceptionEntryPoints);
|
|
17666
|
-
});
|
|
17667
|
-
});
|
|
17668
|
-
}
|
|
17669
17617
|
|
|
17670
|
-
doOnConnection(self, clientSocket);
|
|
17671
|
-
};
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
17675
|
-
// FOR THE WEBSOCKET IMPLEMENTATION :
|
|
17676
|
-
this.clientSocket.addEventListener("open",doAllOnConnection);
|
|
17677
|
-
}else{
|
|
17678
|
-
// FOR THE SOCKETIO IMPLEMENTATION :
|
|
17679
|
-
this.clientSocket.on("connect",doAllOnConnection);
|
|
17680
17618
|
|
|
17681
|
-
// Client ping handling :
|
|
17682
|
-
// (SocketIO implementation only, the websocket implementation sends back a hidden pong !)
|
|
17683
|
-
// PING-PONG :
|
|
17684
|
-
this.receive("protocol",(message)=>{
|
|
17685
|
-
if(message.type!=="ping") return;
|
|
17686
|
-
self.send("protocol",{type:"pong"});
|
|
17687
|
-
// // DBG
|
|
17688
|
-
// lognow("DEBUG : CLIENT : Pong sent.");
|
|
17689
|
-
});
|
|
17690
|
-
}
|
|
17691
|
-
|
|
17692
|
-
|
|
17693
|
-
|
|
17694
|
-
}
|
|
17695
|
-
|
|
17696
|
-
}
|
|
17697
17619
|
|
|
17698
17620
|
|
|
17699
17621
|
/* INCLUDED EXTERNAL LIBRAIRIES
|