From 65eaa99b81e2eb9f6de89ea17ac799e136ad620e Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 11 Sep 2012 07:24:40 +0800 Subject: [PATCH 001/141] add worklog --- README.md | 2 +- WORKLOG.txt | 6855 +++++++++++++++++++++++++++++++++++++++++++++++++++ backup | 3 + 3 files changed, 6859 insertions(+), 1 deletion(-) create mode 100644 WORKLOG.txt create mode 100644 backup diff --git a/README.md b/README.md index 807173bf..6cdcbb64 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -vast.js +VAST.js ======= VAST.js is the javascript implementation of VAST: a scalable P2P network for spatial publish/subscribe (SPS) \ No newline at end of file diff --git a/WORKLOG.txt b/WORKLOG.txt new file mode 100644 index 00000000..44334afa --- /dev/null +++ b/WORKLOG.txt @@ -0,0 +1,6855 @@ + +To-Do: + +- add VON_REQUEST_ID msg or send VON_HELLO first? (for ID assignment without net layer involvement) +- check if initially only VON_MOVE exists is okay (no VON_MOVE_B for boundary neighbor checks) +- check redundency in messages sent out + + +- check timestamp compare are only for those generated at same host (it's meaningful) +- optimize MOVE event publications (combine event & AOI update into one) +- VoroCast (area publication) + + +Bug List: + +BUG (*) denotes design bug: + +--- +2012-06-23: 1st & 2nd level enclosing neighbors are the same +2012-07-23: voro.overlaps returns 'false' always when there's only one region for any point +2012-08-29: scaling many nodes will cause TCP connection errors +2012-09-03: EADDRINUSE crash bug (after running a while with >50 nodes) + + + +---- +2009-06-12 UDP transmission causes connection lost in real network +2009-07-08 realnet: agent join does not work correctly for over 3 agents or after existing agent departs + + +2009-07-29* if all known AOI peers of a relay's client fail (due to the relay that manages them fail). + after the clients re-join, this relay may be partitioned, as no AOI neighbor would know / be + interested to contact it (so this is a massive failure scenario --> if all your AOI neighbors disappear, + might cause partition) see VAST-case-2009-07-29 + +2009-07-30b some agents would have undeleted objects in multiple arbitrators (probably not receiving OBJECT_D properly) +2009-08-07 after ownership reclaim is enabled, some nodes inside the 1st arbitrator's region would disappear +2009-08-21 avatar objects remain visible after agents have moved out of AOI (caused by having deletion buffer) +2009-09-29 linux server would attempt to re-connect to lost agents and freeze +2010-04-30 too many relays are removed + +- ghost objects at client-view +- each client subscribes more than once, (though may be harmless, but re-subscription should be avoided) +- client crash (in stress test, some clients seem to depart early, about 5%) +- gateway crash + join rate of 1 node / sec and 500 nodes, gateway crashes after roughly 200 nodes + in 300 nodes, 2 sec / join, gateway would seg fault +- join retry timeout is buggy +- 2010-10-06 after repeated join/leave of worlds, cannot join again (gateway could not assign origin matcher) + + +2012-09-11 (2) +-------------- +goal: VAST porting + basic pub/sub functions + layout modular framework (for independent features) + +- basic idea for making VASTClient, VASTRelay, VASTMatcher modular: + +VASTMatcher: + support for pub/sub matching should exist at VASTMatcher + it deals with pub/sub record keeping & matching + VASTMatcher can be implemented with either VON or VSO, + the latter will provide dynamic load balancing among matchers. + if only the former is used, then only static partitioning is available. + + basic matcher functions: + * accept subscribe request (from VASTClient) + * accept publish request (from VASTClient) + * forward publish request to neighbors + * deliver publications to relevant subscribers within own region + * list currently known subscribers + * join a VON network and maintain topology with neighbors + + +VASTRelay: + support physical coordniate identification and connection to closest Relay + +VASTClient: + basic client functions to forward all requests to current "owner matcher" + and get all responses from "owner matcher" (possibly via Relay) + + + + +2012-09-10 (1) +-------------- +goal: VAST porting + basic pub/sub functions + + +- review & learn that a VAST node actually has three components: + VASTClient - end user pub/sub requests + VASTMatcher - server that handles pub/sub matching + VASTRelay - locate and connect with physical neighbors + + need to think about how to make feature-adding modular + + +2012-09-09 (7) +-------------- +goal: VSO porting + basic pub/sub functions + +- create VSO_peer.js file and build initial API methods + + +2012-09-08 (6) +-------------- +goal: VSO porting + basic pub/sub functions + debug asymmetric AOI + +DEBUG: +2012-09-07: aymmetric AOI doesn't work + the asymmetric AOI bug disappears if 'radius' is not updated from webpage input + it means that updating AOI radius thus might have corrupted the data + suspect it's caused by parameter passing in that do not conform to VAST types + > check if all input to VON_peer, will conform to internal data structure format + > seems to solve the problem + + +2012-09-07 (5) +-------------- +goal: VSO porting + basic pub/sub functions + +- allow position (keyboard input) & AOI-radius update to be reflected on GUI + +BUG: asymmetric AOI does not seem to work + + +2012-09-06 (4) +-------------- +goal: run 100+ nodes at Linux server (dev server) + + +BUG -new error when running 100 VONpeers on gateway: EMFILE + +- seems workable for some time, though still will get EMFILE error +after some time (20min+) the program will still be terminated by system + + +2012-09-05 (3) +-------------- +goal: verify can run for long time.. (if all exceptions are caught) + +ECONNRESET error (when other peer disconnects) + +- seems like we can run for sometime before running into the 10055 error + + { [Error: connect Unknown system errno 10055] + code: 'Unknown system errno 10055', + errno: 'Unknown system errno 10055', + syscall: 'connect' } + +eventually there's the error: + + FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory + +possible cause is that as TCP does not free connected sockets immediately (do so after 240 seconds), +sockets are occupied and not released, causing available sockets to be used up, if frequent connect/disconnect is used + +see: http://www.proxyplus.cz/faq/articles/EN/art10002.htm + + + +2012-09-04 (2) +-------------- +goal: debug scaling connection problems + debug EADDRINUSE + pinpoint EADDRINUSE place +ERCONNR + +- still cannot locate where error occurs +- possibly will change course first and develop support for pub/sub first + + + +2012-09-03 (1) +-------------- +goal: debug scaling connection problems + debug EADDRINUSE + pinpoint EADDRINUSE place + +- try to catch exception in 'net_nodejs.js' by warpping 'exception' & 'catch'. + however, is there a cleaner approach? + +BUG after running 50 nodes for a while.. (using test_VON_scale.js mostly) + a node in test_VON_scale would crash due to EADDRINUSE. + +BUG "no mapping exist for send target" (how did it happen? is it normal?) + + + + + +2012-09-02 (7) +-------------- +goal: debug scaling connection problems + debug EADDRINUSE + +- found out binding to default 37700 port could fail (no response) after already binded + + + +2012-09-01 (6) +-------------- +goal: debug scaling connection problems + debug EADDRINUSE + +process.ic +- learn that EADDRINUSE is caused by binding to same port. might be caused by + running out of ports + +- 50 nodes on NB is fine, but not 100 nodes + +2012-08-31 (5) +-------------- +goal: debug scaling connection problems + pinpoint cause of individual errors (localize it to code sections then catch it) + +- appears nodes would overlap on same positions (possibly the cause for the voro site < actual nodes problem) + + + +2012-08-30 (4) +-------------- +goal: debug scaling connection problems + + +BUG run 200 nodes at localhost of windows client (crash due to error 10055). + many errors if running for prolonged time + + while running: + EADDRINUSE + no mapping exist for send target XX + + when lots of nodes disconnect: + ECONNRESET + ECONNREFUSED + + +- ports currently binded (used) for a few selected nodes: 6, 9, 24, 20 (running 50 nodes) + so neighbor count seems to be in the 'alright' range + + +2012-08-29 (3) +-------------- +goal: massive node movement demo + +MILSTONE +- catch all exception errors, now can relatively stably run to 200 nodes at dev server + +BUG: appears there are problems with frequent connect/disconnect + after a while many errors still to appear, some could crash the program + + + +2012-08-28 (2) +-------------- +goal: massive node movement demo + + +- port clusterMovement from C++ code in VAST to javascript + + + +2012-08-27 (1) +-------------- +goal: build connector to server-side vast node via websocket + basic GUI control in browser + debug 2nd node see only one neighbor + + +DEBUG 2012-08-17: vast.io server hangs after several clients join (clients > 3) + + > the enclosing neighbor not found bug is caused by having boundries set incorrectly (the top boundary should be + smaller than 'bottom' boundary) + +DEBUG when nodes leave, still existing nodes do not see their departure + > two causes: + 1. removeNonOverlapped is not called (now try to call it periodically during tick) + 2. sendBye does not work properly (when a client disconnects from vast.io server, it should + generate a disconnect event, which should in turn cause leave() be called, + and sendBye be called + +MILESTONE + seems like node join / leave / neighbor discovery all works properly! ^^ + basic GUI also works (movement & display) + + +2012-08-26 (7) +-------------- +goal: build connector to server-side vast node via websocket + basic GUI control in browser + debug 2nd node see only one neighbor + +BUG: seems like no halfedge is produced after 2nd node joins + (via vast.io mechanism), so node [1] is not enclosing neighbor + to node [2] + > test with test_VON_peer, appears that there's also 0 halfedge after + 2nd node joins (yet it still returns node [1] as enclosing neighbor) + + +2012-08-25 (6) +-------------- +goal: build connector to server-side vast node via websocket + basic GUI control in browser + +- vast.io client&server function implemented + + + +2012-08-24 (5) +-------------- +goal: build connector to server-side vast node via websocket + basic GUI control in browser + debug VON_QUERY forwarding / neighbor discovery + + +DEBUG initial neighbor check by GW does not notify for enclosing neighbors to a joiner +2012-08-23: some nodes only connect with gateway + + > seems like the problem is caused by GW node, not able to maintain enclosing neighbor relations properly + so when it tries to notify additional new neighbor first to a joiner, it only finds itself as being relevant + + > after some probing, seems like the EN mechanism at GW is somewhat flawed. + where only GW itself is identified as the EN for a newly joined node. appearantly EN list is not built properly + + > it's found because the boundary range in vast_voro is set to [10000, 10000] but the acutal nodes + move to negative coordinates, the Voronoi computation thus is only partial. The range is now enlarged to + [-10000, 10000]. seems to work better + + +2012-08-23 (4) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + basic GUI control in browser + debug multiple logins via HTML cannot get IDs + +DEBUG 2012-08-22 + found out when using MOVE to update position to other neighbors, as 'addr' field is empty, + but it's also updated to other nodes, so original addr is erased. + solved by updating how MOVE data is being processed, so only provided fields will replace old (not empty ones) + +BUGS: + 1. some nodes only learn of gateway + 2. all nodes always connect to gateway + 3. no neighbor discovery is done beyond original contact from server + + +2012-08-22 (3) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + basic GUI control in browser + debug multiple logins via HTML cannot get IDs + +- seems like joining works, but nodes keep add up.. num of neighbors is not bounded + +BUG: some address returned by gateway is invalid, so contacting them would fail for a joining node. + + + + +2012-08-21 (2) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + basic GUI control in browser + debug multiple logins via HTML cannot get IDs + +- modify join() procedure in VON_peer a bit, so that listen to + socket and detecting self IP are both done, before returning + a join success + + +2012-08-20 (1) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + basic GUI control in browser + debug multiple logins via HTML cannot get IDs + +BUG: +- appears stuck at when 4th node joins, some messages are passed from gateway to other exiting nodes (but fail). However, for some reasons these traffics are blocked so can't tell. + + +2012-08-19 (7) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + basic GUI control in browser + debug multiple logins via HTML cannot get IDs + +- still tracing multiple logins, appears 1st node is correct, onto 2nd node.. + + + +2012-08-18 (6) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + basic GUI control in browser + debug multiple logins via HTML cannot get IDs + +- tracing join procedure to discover problem + + +2012-08-17 (5) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + basic GUI control in browser + debug multiple via HTML cannot get IDs + +- change join procedure a bit, HTML client only specify vast.io server's IP & port to connect + whereas within the vast.io server, the VON gateway IP/port is specified + +BUG after 3 clients, vast.io server appear to be frozen. + + + +2012-08-16 (4) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + implement client-side vast binding (socket.io based API) + basic GUI control in browser + socket.io-style vast.io interface + +- connect through HTML -> vast.io -> socket.io VAST node server -> VON peer + and get self ID + +BUG: if multiple webpages are opened, seems like it gets messed up, + additional client IDs can be assigned, but not always... + + +2012-08-15 (3) +-------------- +goal: build connector to server-side vast node via websocket + debug VON_QUERY forwarding + implement client-side vast binding (socket.io based API) + basic GUI control in browser + +- try to imitate socket.io's implementation to produce vast.io +- study difference between module.exports & exports in node.js + + +2012-08-14 (2) +-------------- +goal: build connector to server-side vast node via websocket + implement client-side vast binding + debug VON_QUERY forwarding + +- make vast_voro & LOG work under both browser & node.js environment +- setup GUI environment to integrate with socket.io client-side API + + +2012-08-13 (1) +-------------- +goal: build connector to server-side vast node via websocket + implement client-side vast binding + debug VON_QUERY forwarding + self connection as gateway + +- self connection as gateway ok +- finish TODO: + * remove 'id' 'VoronoId' for node.aoi.center when sending outbound messages + + +2012-08-12 (7) +-------------- +goal: build connector to server-side vast node via websocket + implement client-side vast binding + debug VON_QUERY forwarding + self connection as gateway + + + +2012-08-11 (6) +-------------- +goal: build connector to server-side vast node via websocket + implement client-side vast binding + debug VON_QUERY forwarding + +- try to allow client connect to itself as gateway if it's the first + node to connect + +2012-08-10 (5) +-------------- +goal: build connector to server-side vast node via websocket + implement client-side vast binding + +- multiple browser clients can now login and create VON peers + +BUG +- semes like VON_QUERY forwarding is not done correctly, + new nodes aren't getting the VON_NODE messages they need to confirm + joining. + +DEBUG +2012-08-09: cannot bind port continously in VONpeer + main problem is that only net_nodejs can report bind success/failure + but makes more sense to do re-try in vast_net + so need to be careful of who does what + + +2012-08-09 (4) +-------------- +goal: build connector to server-side vast node via websocket + implement client-side vast binding + +BUG +- VON peer cannot attempt to bind ports continously (event mishandled) + + +2012-08-08 (3) +-------------- +goal: build connector to server-side vast node via websocket + implement server-side vast functions + implement client-side vast binding + +- finish server implement +- rough client version, tested joining success + +BUG +- on second join will join as server still, causing binding to fail & server crash + + + +2012-08-07 (2) +-------------- +goal: build connector to server-side vast node via websocket + implement server-side vast functions + +- implement socket.io server join/leave/move functions + + + +2012-08-06 (1) +-------------- +goal: build connector to server-side vast node via websocket + +- build a socket.io + VON_peer test client, also define events the client will send + to this "connector" in order to join a VON. + +- add continous port re-binding until successful to vast_net +- add return true/false when listening to port for net_nodejs + + +2012-08-05 (7) +-------------- +goal: build websocket test sample + +- run sample socket.io successful, can now run server with node.js @ Windows +- can also send custom message back & forth (sending a message + a js object) + + +2012-08-04 (6) +-------------- +goal: porting: VON_peer.js + test _checkNeighborDiscovery() + test _removeNonOverlapped() + +- study how to use websocket + + +2012-08-03 (5) +-------------- +goal: porting: VON_peer.js + test _checkNeighborDiscovery() + _removeNonOverlapped() + +- implement all VON functions (pending tests) + + +2012-08-02 (4) +-------------- +goal: porting: VON_peer.js + VON_MOVE processing + _checkNeighborDiscovery() + _removeNonOverlapped() + +- notice that when the network has just few nodes, movements are sent via MOVE (not MOVE_B), so meaning that + not explicitly neighbor discovery check will be performed. Need to confirm is this ok behavior. + (possibly okay, as EN will suffice to discovery, only later will boundary neighbor be important as node size increases) + +- do not use 'typeof' to check whether a js object has certain thing (in order to further decode) + use 'hasOwnProperty' to check + +- implement checkNeighborDiscovery() (pending test) + + + + +2012-08-01 (3) +-------------- +goal: porting: VON_peer.js + VON_MOVE + _checkNeighborDiscovery() + _removeNonOverlapped() + +- make sure a remote disconnect will generate a proper VON_DISCONNECT +- finish implement move() +- review & move all C++ codebase to js (but comment out non-functional parts) + + + +2012-07-31 (2) +-------------- +goal: porting: VON_peer.js + debug same pos no EN issue (still need to join) + +DEBUG +2012-07-30: if two nodes have same position, raymond hill's voronoi produce site for just one node + this impacts neighbor discovery correctness + +- solution: send VON_HELLO & VON_EN separately, so a new node still sends HELLO + even if its EN list for the remote node is empty (can happen if both + nodes occupy the same position, one of them won't get recorded into + the voronoi object, causing inproper judgment) + +- VON_EN works + + +2012-07-30 (1) +-------------- +goal: porting: VON_peer.js + test leave(), + debug VON_HELLO + (get_en does not return any neighbor when the first client node joins) + +BUG: +IMPORTANT NOTE: +- found out if two sites are located at the exact same place, + under Raymond Hill's implementation, only one 'cell' will result. + this may have implication for the join procedure, as when a node result its initial + neighbor list via VON_NODE, if just two nodes exist and occupy the same location + only one cell exist and no enclosing neighbor are there. therefore the + 2nd joining node will not be able to contact the first gateway for join + +- implemented VON_HELLO, VON_HELLO_R, VON_BYE +- leave works properly + + +2012-07-29 (7) +-------------- +goal: porting: VON_peer.js + test leave(), + debug VON_HELLO + +- modify vast_voro internals so that external queries/checks (get_en, + contains, overlaps) will perform recompute() first and check for + node id correctness, before moving further. + + + +2012-07-28 (6) +-------------- +goal: porting: VON_peer.js + test leave(), + debug VON_HELLO + +- try to debug get_en() does not return as expected (mismatch between what's passed + and what's stored: string vs. number) + learn that storing things as keys in a {} map will turn key into 'string' +2012-07-27 (5) +-------------- +goal: porting: VON_peer.js + test leave(), + process _new_neighbors array + debug leave undefined target (implement tick()) + +- implement checkNewNeighbors + +BUG +- VON_HELLO does not seem to work correctly when discovering new neighbors + + + +2012-07-26 (4) +-------------- +goal: porting: VON_peer.js + test leave(), + process _new_neighbors array + +- implement init data structure, leave() +- make leave work & send neighbors VON_BYE +- right now new neighbors are not processed for VON_NODE, need to fix this. + + +2012-07-25 (3) +-------------- +goal: porting: VON_peer.js + test leave(), + allow assignment of new id from gateway + process _new_neighbors array + +- connect success callback added +- change test client to connect to remote gateway +- can get new ID from gateway, at both network layer & VONpeer +- add getID() function to get selfID at vast_net + + + +2012-07-24 (2) +-------------- +goal: porting: VON_peer.js + test join(), leave(), + implement VON_QUERY response + allow assignment of new id from gateway + +- join done +- implemented VON_NODE (processing new_neighbors pending) +- add new VAST type: ratio + +2012-07-23 (1) +-------------- +goal: porting: VON_peer.js + test join(), leave(), + implement VON_QUERY response + allow assignment of new id from gateway + +- sendNodes seems to work :) +- allow VAST.pos to inherent from point2d (to have 'distance' function) + +BUG: +- isRelevantNeighbor seems to produce different results if overlap + test is chosen to be 'accurate' vs. 'non-accurate'. with the 'accurate' + result not passing overlap test... (a bit strange because a site's position + should overlap with its region). + + +2012-07-22 (7) +-------------- +goal: porting: VON_peer.js + test join(), leave(), + implement VON_QUERY response + allow assignment of new id from gateway + +- done porting helpers: + _sendEN + _sendHello + _sendNodes + + +2012-07-21 (6) +-------------- +goal: porting: VON_peer.js + test join(), leave(), + implement VON_QUERY response + can determine node id of the remote node sending the message (network-layer?) + +- now when init connection to remote node, connecting node will send its 'id' + first as handshake. the listening node can then learn of the connecting node's id. + however, if no remote id is provided (0, or VAST_ID_UNASSIGNED), then listening + node will assign internal IDs (-1, -2, -3...) to send back messages + + +2012-07-20 (5) +-------------- +goal: porting: VON_peer.js + test join(), leave(), + implement VON_QUERY response + can determine node id of the remote node sending the message (network-layer?) + +- exchange node id when connecting (in progress) + + + + +2012-07-19 (4) +-------------- +goal: porting: VON_peer.js + test join(), leave(), implement VON_QUERY response + +- finished implement VON_QUERY +- BUG: now enters infinite loop (forwarding request to another node) + bug occurs because from_id is [-1] (cannot yet see remote node's self id) + + + +pending: need to implement: + +_isRelevantNeighbor +_isTimelyNeighbor +_sendNodes +_aoi_buffer + + + +2012-07-18 (3) +-------------- +goal: porting: VON_peer.js + test join(), leave(), implement VON_QUERY response + +rename: + IPaddr to addr (vast_addr) + addr to endpoint (vast_endpt) + g_log to LOG + +- implement insertNode() (still in progress) + + +2012-07-17 (2) +-------------- +goal: porting: VON_peer.js + test join(), leave(), implement VON_QUERY response + +- make deserialize into VAST objects work for all basic types + + +2012-07-16 (1) +-------------- +goal: porting: VON_peer.js + test join(), leave() + +- test out join's send msg to server successful +- in process of implementing VON_QUERY's server response + + +2012-07-15 (7) +-------------- +goal: porting: VON_peer.js + join(), leave() + +- implement join() & send message within VON_peer + + +2012-07-14 (6) +-------------- +goal: porting: VON_peer.js + join(), leave() + +- add 'disconnect' to vast_net + +BUGFIX: +2012-07-11: vast_nodejs cannot notify for client socket disconnection (can't identify which) + + + +2012-07-13 (5) +-------------- +goal: test refactored net_nodejs + +- makes it work (vast_net) +- a problem is that if connection breaks using 'disconnect', + two disconnect events are fired ('end' and 'close') + but if the connection is broken by force (via Ctrl-C) only one disconnect is fired + (only 'close' is fired but not 'end') + + +2012-07-12 (4) +-------------- +goal: porting: VON_peer.js + join(), leave() + +- start revising net_nodejs internal implementation (refactor to use less code) + + +2012-07-11 (3) +-------------- +goal: porting: VON_peer.js + join(), leave() + + +- extract common data types as a global VAST object (vast_types.js) +- remove requirement to provide a network layer when creating VON_peer + +BUG: +- found out vast_nodejs doesn't seem to be able to notify client socket + disconnection for a listening server + + + +2012-07-10 (2) +-------------- +goal: porting: VON_peer.js + join(), leave() + +- a first version, very simple example case of using VAST & VONpeer, test_VON_peer.js + (all includes are available once 'common.js' is required) + + + +2012-07-09 (1) +-------------- +goal: porting: VON_peer.js + join(), leave() + +- write constructor test case +- define some common data structures (area, addr, pos, IPaddr, node) + + +2012-07-08 (7) +-------------- +goal: implement VON_peer.js + +- done constructor & VON message enumation + + +2012-07-07 (6) +-------------- +goal: define VON_peer.js interface + +- done initial version + + +2012-07-06 (5) +-------------- +goal: define vast.js interface + +- to design: whether world_id should be inherent to a given gateway + (that is, no need to assign it when joining, the gateway simply setup + a unique world_id) + +- finished defining interface + +2012-07-05 (4) +-------------- +goal: implement vast_net.js + test send() & register() + +- remove register(), put as part of constructor + +MILESTONE: +finish first version of vast_net, supporting socket send & listen + + +2012-07-04 (3) +-------------- +goal: implement vast_net.js (major net-related functions required by VAST) + +- implemented send(), register(), storeMapping() + + +2012-07-03 (2) +-------------- +goal: implement vast_net.js (major net-related functions required by VAST) + +- define vast_net's interface + + +2012-07-02 (1) +-------------- +goal: debug net_nodejs.js + +- finished :) + + +2012-07-01 (7) +-------------- +goal: implement working net_nodejs.js + +- finish 1st implementation (buggy & not yet working) + + +2012-06-30 (6) +-------------- +goal: implement working net_nodejs.js + +- layout net_nodejs main functions, ready to implement + + +2012-06-29 (5) +-------------- +goal: layout basic functions / interface for vast_net + (based on c++ version) + +- orgnaized some functions from VASTnet in VAST C++ + + + +2012-06-28 (4) +-------------- +goal: finish graphical demo for vast_voro.js (boundary neighbors, AOI-radius) + +MILESTONE: +- done ^^ +- can display enclosing & boundary neighbors (correctly & switchable) + + + +2012-06-27 (3) +-------------- +goal: provide a graphical demo of the new vast_voro.js + +- can now detect which id the mouse covers +- can display enclosing neighbors ^^ + + +2012-06-26 (2) +-------------- +goal: provide a graphical demo of the new vast_voro.js + +- convert test_voro_rh to use vast_voro.js (works) +- able to display mouse position + + +2012-06-25 (1) +-------------- +goal: rh_voronoi: port closest_to() and rest + +- done ^^ + +MILESTONE: +all vast_voro.js methods have been ported with rh_voronoi as the underlying mechanism :) + + +2012-06-24 (7) +-------------- +goal: rh_voronoi: port overlaps + debug 2012-06-23 bug: 1st & 2nd level enclosing neighbors are the same + +- done (but havn't checked visually) +- appears that accurate & non-accurate mode of overlap check + produces very different results (so should use accurate mode) + +- also separate each unit test into function forms + + +2012-06-23 (6) +-------------- +goal: rh_voronoi: port get_en() + +- done (for 1st level). also optimizes to use simpler procedure + +BUG +- but when going to 2nd level enclosing neighbor, sometimes + both 1st & 2nd level neighbors are the same set. + + +2012-06-22 (5) +-------------- +goal: rh_voronoi: port is_enclosing() & get_en() + +- is_enclosing() appears to work correctly :) after calling + getNeighborSet() directly + + +2012-06-21 (4) +-------------- +goal: port is_boundary() in rh_voronoi + + +- is_boundary() and enclosed() and both compile and run correctly +- however, found that simply !enclosed() does not necessarily + produce a 'boundary neighbor" +- need other solutions to identify / calculate a boundary neighbor + + +2012-06-20 (3) +-------------- +goal: debug contains() in rh_voronoi + +- confirm that the halfedges sent within 'result' of the rh_ovoronoi class, +simply are "edges" forming a circle around thep (with bounding boxes as the end of edges) + +- done insideRegion() ^^ +- replace with a correct & possibly faster algorihtm from: + +http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + +2012-06-19 (2) +-------------- +goal: debug contains() in rh_voronoi + +- check insideRegion() against java version, seems to be the same +- stuck at not knowing how to solve insideRegion()'s correctness + + +2012-06-18 (1) +-------------- +goal: debug contains() in sf_voronoi + +line general form: +ax + by + c = 0 + +slope-intercept form: +y = mx + b + +givn two points (x1, y1) (x2, y2) +slope: +m = (y2-y1) / (x2-x1) + +intercept: +(y2-y1)/(x2-x1)*x1 + b = y1 + +y = y1 + [(y2 - y1) / (x2 - x1)]¡P(x - x1) +(x2 - x1)¡P(y - y1) = (y2 - y1)¡P(x - x1) + +(x2¡Py - x1¡Py) - (x2¡Py1 - x1¡Py1) = (x¡Py2 - x¡Py1) - (x1¡Py2-x1¡Py1) +x2¡Py - x1¡Py - x2¡Py1 + x1¡Py1 = x¡Py2 - x¡Py1 - x1¡Py2 + x1¡Py1 +(y2-y1)x + (x1-x2)y + (x2¡Py1 - x1¡Py1 - x1¡Py2 + x1¡Py1) = 0 + +a = (y2-y1) +b = (x1-x2) +c = (x2¡Py1 - x1¡Py1 - x1¡Py2 + x1¡Py1) + +- contains() work with rh_voronoi, but still buggy + (check returns 'true' very often) + + +2012-06-17 (7) +-------------- +goal: modify vast_voro to work with sf_voronoi.js + +- finished converting contains & insideRegion (runnable) but buggy + (edges surrounding a cell appears to be all empty) + + +2012-06-16 (6) +-------------- +goal: modify vast_voro to work with commonly exposed Voronoi data structures (esp. from rh_voronoi) + first to work with SFVoronoi + +- got sorted sites to expose in vast_voro (from both sf_voronoi & rh_voronoi) +- started to convert contains() (still has errors) + + +2012-06-15 (5) +-------------- +goal: modify vast_voro to work with commonly exposed Voronoi data structures (esp. from rh_voronoi) + +RBTree methods: + +rbInsertSuccessor(node, successor) +rbRemoveNode(node) +rbRotateLeft(node) +rbRotateRight(node) +getFirst(node) +getLast(node) + +- calculating basic Voronoi ok by using vast_voro.js + + +2012-06-14 (4) +-------------- +goal: modify vast_voro to work with commonly exposed Voronoi data structures + +- found out seg and vertexIndex (used by elements in mEdges) are actually defined & stored in 'line2d' data structure + + + +2012-06-13 (3) +-------------- +goal: make vast_voro work with sf_voronoi + +- note what is required from sf_voronoi (used by vast_voro) + +compute(sites, bbox) calculates Voronoi diagram, returns result with edges & cells +get_idx(id) get the index in mSites given an id of the site + +mSites list of voronoi sites (sorted) + site_num id of the site (user-specified ID) + coord position of site + edge_idxlist list of id's of today's working + +mEdges + a,b,c parameters to define a line (edge) + vertexIndex two vertices forming the edge, used by collides() + seg.intersect + bisectingID + +mVertices +result contains a list of calculated results (sorted sites & drawn edges) + + +2012-06-12 (2) +-------------- +goal: separate vast_voro from sf_voronoi + +DEBUG: executing functions from sf_voronoi.js from vast_voro.js + causes the required script to be printed out + + > need to 'new' the object before use (otherwise it's treated + as a simple script file) + + +2012-06-11 (1) +-------------- +goal: separate vast_voro from sf_voronoi + +DEBUG: infinite loop was caused by min/max x & y values not assigned correctly + +- done + + +2012-06-10 (7) +-------------- +goal: separate vast_voro from sf_voronoi + +- revise sf_voronoi.js to behave same as rhill-voronoi-core.js + but when calculating Voronoi, program hangs (infinite loop) + + +2012-06-09 (6) +-------------- +goal: separate vast_voro from sf_voronoi + +supported functions: + +insert(id, pos) +remove(id) +update(id, pos) +get(id) +clear() + +contains(id, pos) --> v insideRegion(index, p) +is_boundary(id, pos, radius) --> v enclosed() +is_enclosing(id, center_id) --> get_en() +get_en() --> v getNeighborSet() +overlaps(id, pos, radius, mode) --> v collides() + +needed functions from sf_voronoi (to be used within compute()) + +clear() +insert() +getedges() or recompute() & access to mEdges + + + + + +2012-06-08 (5) +-------------- +goal: adopt sf_voronoi to use test_voro_html5.html + +- done ^^ + +2012-06-06 (3) +-------------- +goal: test applicability of raymond hill's voronoi implemention to VAST + + +raymond hill functions: + +var sites = [{x:300,y:300}, {x:100,y:100}, {x:200,y:500}, {x:250,y:450}, {x:600,y:150}]; +var bbox = {xl:0, xr:800, yt:0, yb:600}; +result = voronoi.compute(sites, bbox); + +result.edges +result.cells +result.execTime + +edge.lSite +edge.rSite +edge.va +edge.vb + + + +needed functions: + +v insert(id, pos) +v remove(id) +v update(id, pos) +v get(id) + +contains(id, pos) --> insideRegion(index, p) +is_boundary(id, pos, radius) --> enclosed() +is_enclosing(id, center_id) --> get_en() +get_en(id, level) --> getNeighborSet() +overlaps(id, pos, radius, mode) --> collidess +clear + +v closest_to(pos) +getedges() +get_bounding_box() +get_sites() +get_site_edges(id) +getstat() + + + + +2012-06-05 (2) +-------------- +goal: convert a generic net layer + +- make sfvoronoi.js usable under both web & node.js + + +2012-06-04 (1) +-------------- +goal: make web demo + +[Milestone] + +- finished rough web demo with performance stat record: + + 100 sites 20 - 30ms + 1000 sites 220 - 240ms + 10000 sites 1400ms + +in comparison, raymond hill's version: + + 100 sites < 10ms + 1000 sites 30 - 80ms + 10000 sites 300ms + + + +2012-06-01 (5) +-------------- +goal: debug get empty edges problem + +DEBUG: found out it's caused by accessing public variables this.mEdges with "this.mEdges" + (should use l_mEdges when accessing public variables from private functions) + + + + +2012-05-31 (4) +-------------- +goal: check sfvoronoi.js against java version + + +MILESTONE: runnable for 1000 sites! :) + +DEBUG: found out some inconsistent assignment between 'null' vs 'undefined' (should use 'null' for some) +BUG: edges are calculated but become empty when retrived later. + + + + +2012-05-30 (3) +-------------- +goal: check sfvoronoi.js against java version + +- found bug! + after ELhash is initialized with 'null' crash bug disappear + but appears again if Halfedge is init with certain values as default + +- check till ELinsert() + + +2012-05-29 (2) +-------------- +goal: check sfvoronoi.js against java version + +- found bug in Java version! + + in function: + private void clip_line (Edge e) + + y1 = e . c - e . a * x1; + +should be: + + y1 = e.c - e.a * x1; + + +- check through clip_line() + + + +2012-05-28 (1) +-------------- +goal: check sfvoronoi.js against java version + +- check till intersect() + + +2012-05-27 (7) +-------------- +goal: check sfvoronoi.js against java version + +- handle small bug in getNeighborSet (access to 'edge' is incorrectly placed) +- recompute(), insideRegion() + +2012-05-26 (6) +-------------- +goal: check sfvoronoi.js against java version + +- is_enclosing(), get_en() + + +2012-05-25 (5) +-------------- +goal: check sfvoronoi.js against java version + +- is_boundary() done + + +2012-05-24 (4) +-------------- +goal: check sfvoronoi.js against java version + +- found the 'sites' data struct should contain 'coord' parameter + but currently it's just a 2D pos (but it's ok?) + +2012-05-23 (3) +-------------- +goal: check sfvoronoi.js against java version + +- done through intersect() + + + +2012-05-20 (7) +-------------- +goal: debug sfvoronoi.js + +DEBUG: when calling nextone() + it should return the sorted sites (from mSites) but currently the + code uses th elements from 'sites' (originally inserted, unsorted). + + also address another potential bug, where the idx2id structure was + used incorrectly (a Hash object, but uses [] to access directly elements) + should use .set .get.. + > fix this by changing to use js array [] directly + + +2012-05-19 (6) +-------------- +goal: debug sfvoronoi.js + +- check whether the == check for halfedge object indeeds checks for equivalance. + found out it's okay. will now need to trace from start and see what's causing the + 'null' ELedge + + +2012-05-17 (4) +-------------- +goal: debug sfvoronoi.sf + +- modify sites to use just plain javascript {} instead of custom Hash object + (tested ok, but the null ELedge bug still exists) + + +2012-02-21 (2) +-------------- +goal: debug sfvoronoi.sf + +MILESTONE: solve a big sfvoronoi.js bug (lasting 4 weeks!) +DEBUG: some pointers appear to be null (cannot access its child members) + > found out it's because the DELETED marker (an Edge object) was changed to null + > but it's actually used for some purpose, so still needs to be in place + + + + +2011-10-24 (1) +-------------- +goal: re-build VAST on Linux (redhat) + +got VAST & ACE (6.0.0 download to test machine), had problem building ACE: + +/usr/bin/ld: .shobj/Local_Name_Space.o: relocation R_X86_64_32 against `std::nothrow' can not be used when making a shared object; recompile with -fPIC +.shobj/Local_Name_Space.o: could not read symbols: Bad value + + + + +2011-07-26 (2) +-------------- +partitioning methods & approaches + +resizable cells - triangle strips, Voronoi +split-merge - quadtree +micro-cell - squares / triangles + + +VAST.js Goals +------------ +Clean, Fast, Solid + +Support Commercial-grade Network Support +- network monitoring +- transaction logging +- server validation +- secure transaction +- complete knowledge of operations +- comparable user experience +- easy to integrate +- switch between P2P & C/S mode + + + +2011/05/24 (4) +-------------- +goal: allow server to listen to port and accept multiple incoming connections + +- allow server to auto-look for available ports after a given port number + + + +2011/05/04 (3) +-------------- +goal: define & implement network-layer API + +* what's needed in a 1st version? + +- GUI display to see nodes / Voronoi / movement +- basic VON functions (join / sub / move / list) +- stat collection (for feedback / performance evaluation) + +* considerations: + +- should also consider the ease of porting to other environments (server/web client/Unity client) + +most basic (GenericNetwork) +socket + connect (addr) + send (msg, size) + disconnect (id) + +callback + received (msg, size) + disconnected (id) + +* first target: +a client to establish TCP/IP connection + + + +2011/04/21 (4) +-------------- +goal: start VAST.js project :) define network layer + +Needed functions for network layer. Also need to assume ID assignment mechanism exists. + +- store ID-to-addr mapping +- VON send/recv to target +- socket send/recv to target +- get time +- get ID +- get states +- get stats + + + +// basics +start() +stop() +storeMapping(in: id, Addr) +sendMessage(in: target, msg, reliable, headertype) +receiveMessage(out: host, msg) +flush() +process() + +// sockets +openSocket +sendSocket +receiveSocket + +getTimestamp +getIPfromHost + +// state query +isJoined +isPublic +isEntry +isConnected + +// tools +validateIPAddress +validateConnection +tickLogicalClock + +// get/set +setTimestampAdjustment +setBandwidthLimit +getTimestampPerSec +getConnections +addEntries +getEntries +getAddress +getHostAddress +getUniqueID +getHostID + +// stat +getSendSize +getRecvSize +resetTransmissionSize +recordLocalTarget + + + +2011/03/21 (1) +-------------- +goal: build VAST with SSL support + +Official Instructions: +http://www.dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ACE-INSTALL.html#sslinstall + +Here's the Linux instructions: +from: http://groups.yahoo.com/group/tao-users/message/19352 + +1) Create $ACE_ROOT/bin/MakeProjectCreator/config/default.features with +the following contents: + +ssl = 1 + +2) cd $TAO_ROOT; $ACE_ROOT/bin/mwc.pl TAOACE.mwc +3) Add "ssl = 1" to $ACE_ROOT/include/makeinclude/platform_macros.GNU. +4) Run make from $TAO_ROOT. + +For Windows: +1) Create $ACE_ROOT/bin/MakeProjectCreator/config/default.features with +the following contents: + +ssl = 1 + +2) Edit $ACE_ROOT/bin/MakeProjectCreator/config/MPC.cfg + with following lines only: + + dynamic_types = $ACE_ROOT/bin/MakeProjectCreator, $?DDS_ROOT/MPC, $?TAO_ROOT/MPC + main_functions = cplusplus:ACE_TMAIN + default_type = vc10 + +3) cd $ACE_ROOT/ace + run $ACE_ROOT/bin/mwc.pl ace.mwc + +4) open the following with VC10 and build project "SSL" + $ACE_ROOT/ace/ace.sln + +Make sure OpenSSL build libraries are installed properly first, can be downloaded here: +http://www.slproweb.com/products/Win32OpenSSL.html + +Note: download the non-light (full) version, so build libraries are included + + +2011/01/05 (3) +-------------- +goal: add matcher promotion according to loading + +to-add: +- load detection +- load reporting +- matcher selection + + +2010/12/28 (2) +-------------- +goal: upgrade ACE in VAST & build with VC 2010 + +BUG many ACE_OS functions (sleep, hostname...) become unavailable in ACE 6.0.0 (appears that OS.h no longer exists) + +- after searching for functions directly in the ACE source code, found that +need to change include "OS.h" to the following: + +#include "ace/OS_NS_unistd.h" // ACE_OS::sleep +#include "ace/OS_NS_netdb.h" // gethostbyname +#include "ace/OS_NS_arpa_inet.h" // inet_ntoa + + + +2010/12/09 (4) +-------------- +goal: sync clocks to gateway for all regular clients + + + +2010/11/23 (2) +-------------- +goal: sync clocks to gateway for all regular clients + + +2010/11/03 (3) +-------------- +goal: use VAST socket to send/recv JSON BCNet messages + +Milestone: +- get VAST socket working, can establish connection with gateway & send, also receive reply, without + having to first create a VAST node (only VASTnet is needed) + + +2010/10/08 (5) +-------------- +goal: refactor VASTnet + +- try to figure out if there's potential thread conflict between incoming / outgoing messages +two levels need to consider: + +1) message queue: inserting and removal of messages of mesage queue, should not affect each other +2) connections: when connecting / disconnecting, the two should not overlap each other + +consider cases: + +1) send & remote_disconnect occurs at the same time -> access to connection object needs mutex + to avoid trying to send, but connection is already invalid + +2) recv & local disconnect occur at same time -> no overlap data structure (should be ok?) + +3) send & local disconnect -> will happen in same thread (no problem) + +4) recv & remote_disconnect -> in same thread (no problem) + +5) disconnect & remote disconnect occur at same time -> access to connection object & handler may conflict + +6) connect & remote connect occur at same time -> duplication connections may result (but will be protected + because socket_connected () & socket_disconnected () will check for duplication + + +2010/10/26 (2) +-------------- +goal: VASTnet refactored & tested + +MILESTONE: 1st refactored VASTnet up & running. + +mostly working alright, using VLC found 28 memory leaks, but all are due to use of ACE (reason unknown) + + +2010/10/07 (4) +-------------- +goal: refactor VASTnet + +- change definition of timestamp_t from uint32_t to uint64_t, also net_ace's getTimestamp () + returns millisecond time since 1970, instead of the program start, this is so that + * times on synchronized hosts can be somehow compared + * systems running VAST do not require reboot every so often (with uint32_t, can only store millisecond for 50 days) + now it's like 584942417 years :) + + +2010/09/21 (2) +-------------- +goal: implement BCNet API based on JSON + +- install JSON with the following steps + * get jsoncpp from http://jsoncpp.sourceforge.net/ + * extract into a path parallel to VAST + * under /makefile/vs71, run jsoncpp.sln and upgrade to VC2008 + * test build lib_json + * include project 'lib_json' into BCNet's solution space + * set lib_json's properties Use of MFC as: "Use MFC in a Shared DLL" + * add to BCNet's include path: "..\jsoncpp-src-0.5.0\include" + * add #include to BCNet.cpp + * add "Json::Value root;" to BCNet.cpp to test creation of JSON objects + + + + +2010/09/14 (2) +-------------- +goal: help integrate BCNet with game client + + +DEBUG gateway's response cannot reach the client + > caused by the message is sent to the client's relay component, but target is 'host_id' instead of 'sub_id' + > added a "self-check", if the send target is my hostID then automatically translate + +BUG gateway leaves sometimes will crash (seems like occurs in receiveMessasge for processing, invalid message + is being processed) + + + +2010/09/14 (2) +-------------- +goal: prepare BCNet wrapper around VAST for use by BlackCat client + +- compiled the class, also wrote a simple demo_console + +BUG if table server is connected immediately when gatewayConnected () is called, it's okay, + but if the joining to table server is triggered by keypress, it hangs in JOINING stage + + +DEBUG after BCNet is ready & run with demo_console for 1st time, we got "R6025 pure virtual function call" + > found out it's because the callback class was defined as local variable in init () (should be global) + otherwise the instance is destroyed when init () finish execution. + + +DEBUG the BCNet project does not produce BCNet.lib and demo_console could not compile + > the BCNet class was not decleared EXPORT so no lib needs to be produced. + +- found out getIPFromHost () in VASTnet would create multiple additional threads + * when calling ACE_OS::hostname () or ACE_OS::gethostbyname () + * when calling ACE_DEBUG for 1st time + * when listening for TCP port (net_ace_acceptor) + * when listening for UDP port (net_ace_handler) + +2010/09/11 (6) +-------------- +goal: separate join procedure into 1) connect to gateway and 2) connect to origin matcher of a particular world + + + + +2010/09/10 (5) +-------------- +goal: run VAST in separate thread, define preliminary game API (on top of VAST) + +- VASTThread done, now we can do ticking in separate thread + + + + +2010/09/08 (3) +-------------- +goal: run VAST in separate thread, define preliminary game API (on top of VAST) + +BUG: seems like if origin matcher was temporily suspended, restoring it will cause various issues + > possible solution is to let all matchers (active / candidate / origin) continously update their status + > to gateway + +2010/09/07 (2) +-------------- +goal: run VAST in separate thread, define preliminary game API (on top of VAST) + + + +2010/09/02 (4) +-------------- +goal: build sample program for event-driven VAST + +- simplify 'demo_console' from 'test_console' + +- extend report () into reportGateway () & reportOrigin () + +BUG tick per second in demo_console is only 16 (should be 20, given the setting) + + + +2010/08/20 (5) +-------------- +goal: externalize VSO backup matcher maintain + +- finish merging matcher pool for VSOPeer & VASTMatcher, now all matchers are centrally managed by + the gateway node (VSOPeer only queries for new matcher from the VASTMatcher component at the gateway) + +BUG appears that the neighbor list is not up-to-date after Matcher crash & take-over + (if no movement, then late joiner will not know existing node, or deleted nodes unremoved?) + + + +2010/08/16 (1) +------------- +goal: externalize VSO backup matcher maintain + + +BUG after origin matcher crash, some issues observed: + - some clients keep staying in state = JOINING and cannot re-join successfully + - clients seem to be able to remove crashed origin matcher (neighbor list unaffected) + but neighbors are updated only when the client tries to contact the failed matcher. + and after failing, the matcher info is removed + +- need to distinguish between join tasks that only need to do ONCE (creating the object, init variables), + and those that may suceed then fail, and could be performed multiple times during the life of the program + (detecting & finding relays, send subscribe request to matcher and get response correctly). + The former can be done once, the latter may need repeated checking & fallback mechanism + + + + +2010/08/13 (5) +------------- +goal: stress test of multiple rooms + +- perform comparison test as follows: + +condition: +gateway & origin on separate nodes +run time: 6000 steps ~ 10min +join interval: 2 sec. + +* reference run (single room) +100 nodes in single world +runtime ~ 13min +max concurrent 84 +leave recorded: 85 + +* multiple rooms run +100 nodes in groups of 4 (25 worlds) +runtime ~ 13:20 +max concurrent 97 +join recorded: 345 +leave recorded: 96 + +basic findings: running in multiple rooms improve max concurrent, though still some nodes appear +to be unable to join. Client crash however has improved (non crashed, appearantly). but some +may fail to join. + +ToDo: +- test ghost client removal in origin matcher fail (backup takes over) case +- avoid client crash +- ensure client always join successfully if gateway still alive +- gateway / matcher can still function properly after temp network problems +- decentralize VSO gateway function + + +2010/08/12 (4) +-------------- +goal: remove client ghosts + +BUG if matcher is not contactable, a joining client may be in JOINING state indefintely + (should somehow switch mode and re-request from gateway, for example) + +DEBUG if a matcher loses origin matcher status due to inactivity, it may re-send keepalive + but its knowledge about origin matcher is still itself, thus may replace the gateway's record, + and make the world to matcher_id id & matchers info inconsistent + > forcefully notify the previous origin that it's not longer the origin + also, would always send a MATCHER_INIT message to either a promoted or demoted origin + +DEBUG client may not be able to send to gateway for any report, if connection is removed + > added check to re-notify the gateway's address, in case of a disconnected gateway + +MAJOR +BUG when the origin matcher is replaced, VSO node still hasn't learned of it, so may when it tries + to promote a new node, the request may be sent to a gateway no longer exist (the departed origin matcher) + somehow the VSO node's idea for gateway should also update, in case of change. + +BUG if a matcher has lost connections to all nodes then resumes it, it tries to re-join to gateway, + but by that time gateway has removed its matcher as well as candidate matcher record, casuing the + matcher unable to join. + +- found that two major cases of fault tolerance needs to be considered: + 1) when nodes permanently fail + 2) when nodes temporaily lose network connection, but then resume + + +2010/08/10 (2) +-------------- +goal: debug matcher fault tolerance + +BUG when origin matchers fail, it seems the gateway will lose the origin matcher record for + the failed matcher's world ID. subsequent JOIN request to gateway thus will be incorrect + + + +2010/08/08 (7) +-------------- +goal: debug matcher fault tolerance + +DEBUG found that the matcher keepalive check would remove nonresponding matchers, + but subsequent MATCHER_ALIVE would not restore it. + > solved by combining MATCHER_ALIVE with MATCHER_JOINED and remove MATCHER_JOINED, so + each time a keepalive is sent, the effect is similiar to notifying a new matcher + preliminary testing on fault tolerance seems to work okay, except the following bug + +BUG when current matcher disconnects, a client's view of neighbors could also be incorrect + (as no matcher can notify for neighbor deletion) + + +2010/07/29 (4) +-------------- +goal: debug matcher fault tolerance + +DEBUG when a origin matcher fails, the backup matcher cannot take over + - sendGatewayMessage in VASTClient would attache sub_id as from field, when gateway tries to + send the new origin matcher info to a VASTClient that sends a JOIN request, as the gateway + doesn't have subID -> hostID mapping, it delivers the NOTIFY_MATCHER to itself + > solved by removing the auto-pending of subID to the client messages' from field + + + +2010/07/22 (4) +-------------- +goal: implement dynamic origin matchers + +- the implement direction is to make VSOPeer stay the same as much as possible, while allowing VASTMatcher + to also manage / handle dynamic matcher joining + +- three peer roles in VSO: + * gateway (records all candidates and choose candidate node to join upon request) + * origin (entry point to a particular VSO space + * peer (a regular node on the network) + + +2010/07/21 (3) +-------------- +goal: allow worlds to be created dynamically + +- some design thoughts / requirements: + * "origin matcher" for a given world can be spawned on demand by gateway + * all capable nodes should contribute (i.e., can become a "origin matcher"), via registeration at gateway + * VAST node joining includes: + 1) notifying candidacy as potential matchers (including origin) to gateway + 2) obtaining the origin matcher of the joining world + * fault tolerance of "origin matcher" should be supported (i.e., failed matcher is taken over smoothly) + * matchers from different worlds share the underlying "relay mesh" & candidate matchers (i.e., a candidate may support any world) + * of course, everything should be done quickly & smoothly + +- implement thoughts: + - VSOPeer should be as simple as possible (close to current form), "worlds" concept should exist outside it. + - should be easy to 'network' different worlds / gateways (share the relay mesh or matcher candidates) + + +2010/07/20 (2) +-------------- +goal: add support of multiple rooms to VAST (add support for optional 'world_id' in join ()) + +- add optional 'world_id' parameter to the join () in VAST interface, so users can choose which "world" to join + + + +2010/07/18 (7) +-------------- +goal: 100+ concurrent without bugs + +DEBUG: # of movements is less than expected + > due to too much sleep in between steps, observe that oversleep could occur + for example, if the desired movements / second is 10, so it's 100ms between each step. + If framerate is 20 loops / second, it's on average 1000 / 20 = 50ms / frame. However, + possible that by 20 frames the elapsed time is 97ms, then a movement would not occur + if we're checking if 100ms has passed. A more precise clock and sleep may help + + > fixed by changing the time units used in TimeMonitor from milliseconds to microseconds + + +DEBUG minimal bandwidth size is unusually large in log under Linux + > fixed by replacing printf parameter from %lu to %l (long unsigned to unsigned) + + + +2010/07/16 (5) +-------------- +goal: debug to run 500+ concurrent actually + +DEBUG: subscription requests may be sent continously to already failed matcher + > all send to matchers now will go through error check, if matcher send is unsuccessful, + it will be removed & replaced + +- stress performance: 300 nodes, 2 sec / join, concurrent 140 +- 100 nodes, 2 sec / join, concurrent 96 + + +2010/07/15 (4) +-------------- +goal: debug to run 500+ concurrent actually + +- will test for 10 minutes runs = 6000 time-steps (10 steps/sec) and 500 nodes, joining at 1 node / sec + (so will take 500 / 60 = 8.3 min to join completely) + + +2010/07/14 (3) +-------------- +goal: stress test VAST to 500+ nodes on physical network + +DEBUG: VASTnet::storeMapping (): existing address and new address mismatch. + > issue may be caused when a host accepts new connection from remote host, + it first records the detected remote port (which differs from listen port). Later when the + remote host is registered again, its listen port is used. So the addresses will be different. + > change to if host accepts connection, the remote host's detected port is registered as '0' (not a listen port) + later when listen port is provided (via a RELAY or SUBSCRIBE message), listen port will be used + if the new replacement port is 0 (caused by a disconnected remote host re-connects again), then + it's considered normal. + +DEBUG: when running test gateway, sleep time is always 25ms (40 frames / sec) regardless of load + > found out it's that VastVerse did not actually use TimeMonitor to ensure it runs only within specified time + budget. Updated to do so. + + + +2010/07/13 (2) +-------------- +goal: stress test VAST to 100+ nodes on physical network + +05/13 +DEBUG: > gateway shows repeating error message + 'net_ace_handler (): UDP message received, but handler's remote_id not yet known' + + try to run 100 clients, the stat was 230 joins and 43 leaves were recorded by gateway + + > made the handling of the UDP error message to terminate UDP handler, run 30 nodes okay (no more error) + however, there was quite a few following warnings: + + VASTnet::storeMapping (): existing address and new address mismatch. + + also, when all clients leave, gateway still considers one client connected, and there was + repeating subscription request sent to certain node (its current matcher?) + + + +2010/06/23 (3) +-------------- +goal: integrate hole-punching to VAST + +DEBUG C# VAST client cannot connect to server, due to SUBSCRIBE message sent to self, would + receive error such as this: + + [13882530681060196353] VASTMatcher::handleMessage () non-Matcher receives Matcher-specific message of type 26 from [13882530681060196353] + + > found out it's because the test program thinks it is a gateway, yet gateway already exists + +DEBUG port is not released even though VAST is shutdown (so init after shutdown does not work) + > found out it's caused by net_ace_acceptor did not call _acceptor.close (); in handle_close () + > a long standing bug addressed :) + + +DEBUG sending a picture of size 63408 bytes would crash the program & gateway + +2010/06/03 (4) +-------------- +goal: deply VASTMapChat on SF with a Linux server + +DEBUG seems Win32 client cannot connect to Linux server due to byte alignment problem + the first ID request message cannot be sent / received properly by the Linux server. + it appears that the length of an VASTHeader is 8 bytes (though should be an unsigned int = 4 bytes?) + on Linux, the ID_REQUEST message is 42 bytes, but Win32 is only 34 bytes. Message from Win32 client + is also received with a several seconds delay. + + > found 'unsigned long' in win32 is 4 bytes, but on linux is 8 bytes, here's some comparison: + > adopt a win32-specific "stdint.h" and unix-default and change all types to explicitly specify size + > also changed size_t in VASTTypes.h to vsize_t (defined as uint32_t, as sizeof (size_t) returns 8 bytes in linux + > Win32 clients then can connect properly to Linux server :) + +align: 4 (win32) +======== +sizeof sizes: +VASTheader: 4 id_t: 8 timestamp_t: 4 length_t: 4 coord_t: 4 +Position: 16 Area: 28 IPaddr: 12 Addr: 28 Node: 72 + +transfer sizes: +VASTheader: 4 coord_t: 4 Position: 12 Area: 20 IPaddr: 8 Addr: 20 Node: 52 + +linux: +======== +sizeof sizes: +VASTheader: 8 id_t: 8 timestamp_t: 8 length_t: 8 coord_t: 4 +Position: 24 Area: 48 IPaddr: 24 Addr: 48 Node: 120 + +transfer sizes: +VASTheader: 8 coord_t: 4 Position: 12 Area: 28 IPaddr: 12 Addr: 28 Node: 72 + + + + + +2010/05/29 (6) +-------------- +goal: make subID receivable at destination when calling VAST's send () + +CHANGE: +make a significant design change to remove automatically build up fromID -> hostID mapping when receiving +incoming messages. So all ID to hostID mapping now need to be explicit (by calling notifyMapping ()) +The change is so that complex mapping won't be built. Also to avoid VASTnet-level auto-forwarding, which +makes interactions complex & difficult to debug + +DEBUG: after removing the mapping, certain later nodes cannot join properly when regions split, + also, matcher node would see more nodes than AOI neighbors. Suspect it's caused by some bad forwarding. + + > solved by make sure matcher also record the ID->hostID mapping for relays of clients, so that + the SUBSCRIBE_NOTIFY message can be properly delivered. The problem was caused by + SUBSCRIBE_NOTIFY unable to deliver properly and be processed by the matcher itself. + + + + +2010/05/25 (2) +-------------- +goal: VAST plugin for Firefox (make join work) + + +MILESTONE: + VAST plugin joins successfully to server.. after creating VAST object into ScriptablePluginObjects + +DEBUG: + Plugin cannot join successfully, also when termintating would crash, found out it's caused by + Plugin being DEBUG mode but VAST.dll is release. If same mode is used for both then there'd be no problem. + + + + +2010/05/20 (4) +-------------- +goal: VAST plugin for Firefox. + +Try to build Win32 samples, got samples build (but some would fail first) but cannot be accessed / used correctly. + + + + +2010-05-14 (5) +------------- +goal: debug problem cases + +DEBUG: +- 05/13 in churn test of 50 nodes, recovery from churn is not consistent (between 95% to 99%, depend on cases) +- 05/14 sometimes subscribe is delayed due to slow relay join + +solved by requiring matcher to notify relay directly of the subID -> client host ID mapping, this way +unknown mapping can be minimized, also addressing the above two problems. Test case reveals that consistency +can recover nicely to 99% for up to 10 nodes join/leave / sec for both 50 & 100 stable nodes + + +2010-05-13 (4) +-------------- +goal: re-do churn test & finish paper writing + +DEBUG +- 05/13 in churn test of 50 nodes, recovery from churn is not consistent (between 95% to 99%, depend on cases) + > appears to be caused by fast switching over regions, so ownership transfer may not be completed + + found a serious problem where the SUBSCRIBE request is forwarded to neighbor matcher, but currently + matcher responds directly to client (will not reach). Changed to respond via relay, but appears + not all relays have yet the mapping setup + + + + +2010-04-30 (5) +-------------- +goal: simulate 1000 nodes + +DEBUG 0.4.4 linux buildable + found out it's caused by the include library order (vastcommon is first, but should be the rightmost as + it's the least dependent) + +- also install a Ubuntu server as Plug server + + +2010-04-29 (4) +-------------- +goal: test fault tolerance + +DEBUG +- 04-28 joining is still slow (relay querying) + force a timeout on relay querying (currently set to 10 requests) afterwards we will force + the client to join, even if Vivaldi's error value is not converged yet + +DEBUG gateway hangs when relays exit + - found it's because there may be RELAY_QUERY coming in disconnected clients + + + + + +2010-04-28 (3) +-------------- +goal: test fault tolerance + +- added command line flag to determine if this node will add as relay or matcher / none / or both + + + +2010-04-23 (5) +-------------- +goal: VAST 0.4.4 release & doc + +DEBUG vastsim_gui shows only '1' as ID + > fixed by building map between node index in VASTsim & subscription ID + + + +2010-04-22 (4) +-------------- +goal: VAST 0.4.4 release & doc + +DEBUG node not moving after relay fail, indefintely + > found that it's caused by the node kept trying to contact an already failed relay + > added mechanism to check if the send is successful (if sent to relays) and if not, + remove the failed relays + +DEBUG nodes strangely do not move, without any matcher/relay failure + > caused by the relay disconnects a client, due to inactivity, as all NEIGHBOR messages are + sent directly from matcher to client. + client may need to PING relays periodically. + + + + +2010-04-20 (2) +-------------- +goal: real network disconnection test via pskill + + +DEBUG: node stop moving after matcher fail (case 2) + > another problem uncovered was that a re-subscribing client checks in with a newly joined + matcher that does not have the client's subscription record, and thus assigns a new subID. + however, the client program is unaware of the subID change, and thus all future movements + are not accepted by the VAST client component + + fix by remove the check, however, this may be dangerous due to redundent ID assignment + TODO: may need a way to periodically re-check ID with the assigning entry point + + + +2010-04-18 (7) +-------------- +goal: debug node stop moving after matcher fail + +DEBUG: node stop moving after matcher fail + > one problem was that when clients try to re-subscribe to new matcher + the new matcher does not consider the re-subscribe client as "owned", and thus + would not send updates to it about its position. + + fixed by forced ownership when accepting SUBSCRIBE request + + another fix was to force notify client of new matcher (MATCHER_NOTIFY) when claiming orphan clients + + + +2010-04-16 (5) +-------------- +goal: churn test + +DEBUG: node 24 does not join properly (it can see others after a slow join, but others can't see it) + > slow join is caused by relay pinging each other, causing physical coord to diverge + > due to the slow relay join, SUBSCRIBE cannot yet be sent, and later when re-subscribe + is initiated, layer number 0 and join location (0,0,0) is used. Causing other neighbors + to not receive updates about the joining node. + + Fixed by recording subscription request regardless of whether the client is ready to + subscribe, so later re-subscribe attempt can be correct. + +BUG: seems like after relay only fail, node movement would beomce dragging (one update every two moves) + +BUG: after matcher fail, some clients stop to have movements + +- Perform churn test (allows one node to join / fail per 1 or 2 seconds, with stable size of 30 or 50) + + main finding is that consistency drops with smaller stable size or higher churn rate (50-60%) + with 50 node as stable size and 2 second join/fail rate, consistency may be around 80%. + + two issues found: + - after failure (of relay?) client movement is dragging + - some nodes would stop to move + + both are consistent with existing problem in failure simulations. + + + +- Added JOIN_RATE to VASTsim.ini, to indicate how many steps before a new node joins +- Added STABLE_SIZE to VASTsim.ini, to indicate # of minium nodes to exist before failure can take place + + +2010-04-15 (4) +-------------- +goal: finish relay / matcher fail sim + +DEBUG: nasty bug where clients belonging to the 3rd matcher joined would not getting update + > found out it's caused by the MATCHER_NOTIFY message sent by previous matcher to clients, + notifying a new matcher, is sent via relay, but relay only pass through MESSAGE / NEIGHBOR messages. + Changed so that any uncongnized message received by relay is treated as a forwarded message to + client + +BUG: connection size is too large + > possibily too many relays are kept. need to periodically clean up + + +- Added MATCHER_SIZE to VASTsim.ini + +2010-04-13 (2) +-------------- +goal: debug relay mechanism + +DEBUG: discover relays do not route properly, problem occurs when relay & matcher exist on + the same node. Matcher may send mssages to relays, which end up is only received by the Matcher's own Client + > route all messages to client to relay first, then it's the relay's task to + forward to clients + > found out it's caused by the relay mechanism not designed properly. Messages sent from + Matcher have subscription ID as destinations. The original idea was to let the network layer + do the subID -> hostID mapping translation. However, as each network layer has only one mapping, + yet proper routing requires two (Matcher -> Relay mapping, Relay -> Client mapping), so it works + if Relay & Client are same node (matcher sends directly to client, in fact bypassing relays), + but if Matcher & Relay are the same node, then the Matcher->Relay mapping turns the message + to itself, but then no mechanism exists to send to Client. + + Resolve the problem by having relay set up a subID -> clientHostID mapping explicitly + (client needs to notify relay of the mapping), and allow the network-layer translation used + only by matcher (for finding relays). Interestingly, NEIGHBOR messages and MATCHER_NOTIFY + (sent by Matcher to Clients) appear can be sent directly without relays, as clients do have + direct connections with their matchers. Other messages such as PUBLISH & SEND need to sent via + relay (as their targets could include clients not managed by the current matcher, so send via + relay is more general). + + +2010-04-12 (1) +-------------- +goal: simulate relay & matcher fault + + +BUG: after sure of making relay records are exchanged correctly, now the traffic increases + (from 27 kb / sec to 35 kb / sec without much change in functionality or correctness) + +BUG: if few relays are specified, it seems like some relay join is not successful + and thus their clients will miss the messages targeted at them.. + +BUG: discover relays do not route properly, problem occurs when relay & matcher exist on + the same node. Matcher may send mssages to relays, which hs + + +2010-04-08 (4) +-------------- +goal: simulate relay & matcher fault + + +DEBUG: found a rare bug that consistency becomes low (90%) in one case of 30 nodes sim (but all others report 98%) + VAST-case-2010-04-08 node 24 join unsuccessful + + > it's caused by a node not joining properly, as its matcher has just joined and not yet initialized + so the SUBSCRIBE messge was lost. + + fixed by first ensuring there's timeout & re-subscribe even for initial join, and also + allow matchers to queue up receivied messages for later processing when its VSOpeer is still joining. + +DEBUG: client cannot send a message and received by itself, but the matcher node to which it sends, whose client + component could receive the message + in other words, only gateway can receive messages sent by VASTnode->send () + + > found it's caused by VASTMatcher saving up the MESSAGE message type instead of forwarding it + to the client. because when for non-matchers, its VSOpeer will not be created, and thus all + messages forwarded to it is saved (potential problem for non-matchers?) + + solution is to simply allow MESSAGE to be processed at non-matchers + + +2010-04-07 (3) +-------------- +goal: simulate relay & matcher fault + +DEBUG: trying to build Linux version for kenny, got this error when linking demo_console + + undefined reference to `vtable + + for the SFVoronoi class, found this explanation: + http://www.daniweb.com/forums/thread114299.html# + + and move the definition of SFVoronoi's destructor to be not inline and solves the linker error + +BUG: linker error, appears to be not specifying the right libraries (however it's not the case...) + +VONPeer.cpp:(.text+0x36bf): undefined reference to `Vast::VoronoiSF::VoronoiSF()' +../../lib/libvast.a(VONPeer.o): In function `Vast::VONPeer::VONPeer(unsigned long long, Vast::VONNetwork*, unsigned long, bool)': +VONPeer.cpp:(.text+0x3917): undefined reference to `Vast::VoronoiSF::VoronoiSF()' +../../lib/libvastnet.a(net_ace.o): In function `Vast::net_ace::receive()': +net_ace.cpp:(.text+0x56d): undefined reference to `Vast::TimeMonitor::getInstance()' +net_ace.cpp:(.text+0x575): undefined reference to `Vast::TimeMonitor::available()' + + + + + +2010-04-06 (2) +-------------- +goal: simulate relay & matcher fault + + +MILESTONE: simulate up to 200 nodes (3000 steps) with 96% consistency and 8 kb / sec bandwidth usage + up to 500 nodes (7000 steps) with 94% consistency and 5.1kb / sec bandwidth + +BUG: but occasionally some nodes may stop and not be moving (not sure why) + +BUG: when building linux version, keep getting the following error: + + /usr/bin/ld: cannot find -lACE + + kenny suggests it's because of the static link flag, but the version MMnet provides + is dynamic only + + +2010-04-05 (1) +-------------- +goal: simulate relay fault + +DEBUG: found for 30 nodes in standard 768x768 world, still some cases where consistency drops due to + inconsistent views among the matchers. + + - fixed by adjusting matcher's AOI, solves the problem while introducing slight overhead + to matcher AOI radius adjustment + + +2010-03-30 (2) +-------------- +goal: experiment matcher fault tolerance + +BUG: found a node would stop getting updates once crossing a region boundary (but not for other nodes) + seems like a new matcher is added afterwards.. + + - it's caused by a complex interaction. The problem is that when subscriber A crosses from + matcher B to matcher C, at matcher C there has actually be some previous ID->host mapping + for subscriber A, such that A is mapped to relay D. What happens is that relay D somehow + disconnects matcher C (timeout?), and matcher C thus removes all mapping reference associated + with relay D (which unfortunately includes subscriber A). Subscriber A's info thus is + removed from matcher C. This removal actually occurs *after* a successful ownership transfer, + so when subscriber A later sends movement updates to matcher C, there will be no processing + & no response. + +MAJOR CHANGE: + - make all time-related tasks consistent to use timestamp for checking + both for countdown or periodic tasks + + getTickPerSecond () changed to getTimestampPerSecond () in VONNetwork + + +2010-03-25 (4) +-------------- +goal: debug & test matcher fault tolerance + + + +2010-03-24 (3) +-------------- +goal: implement matcher fault tolerance + +- client-side object seems to work more properly when backup-to-closest matcher is used + +BUG: still some ghost objects at client side, suspect due to change in region for the client + +BUG: very poor consistency for 50 nodes under 768x768, as low as 50%! + +BUG: insertion of new matcher seems to jump at locations upon joining + +DEBUG: in gui verion the node ID seem to be redundent + - found out it's because we use subscription ID as ClientID, but clients can get subscription ID from + different matchers, changed to display port number as ID, to show it uniquely in GUI + +DEBUG: clients would 'jump' when cross boundaries, fixed by sending movement to both current & closest matcher + + +2010-03-23 (2) +-------------- +goal: finish load balacning + +MILESTONE +load balancing works using the new VSOPeer class. Currently subscription info would only be copied to a neighboring +region if the subscription AOI covers the neighbor region. Found out the most important load balancing design +is to make sure the region center (a VSOPeer node) always follows the center of the load (right now defined as +all 'owned' objects within the VSOPeer's region), this allows the most proper region shape/resizing. +Still an issue is that of the 'ghost objects'.. where replicas still exist though the original has stopped +to send updates to the neighbor region. + +will try use timeout + ping&pong to remove obsolete subscription records (object records in VSOPeer) + +- after removing timeout objects (1 second), ghost objects mostly disappear. However, there are still some + left that do not disappear (possibly due to crossing region boundary so the old matcher has not been + able to notify deletion. + +- nodes also would briefly 'jump' when crossing region boundary, probably due to clients' commands + are not yet sent to the new matcher, so a small inconsistency (pause) while crossing. + (previous VASTATE solution was to send movement to both manager nodes) + +- under 768x768 30 nodes testing, consistency is between 90% - 97%. not very stable. + + +2010-03-15 (1) +-------------- +goal: debug simulation layer (make it work) + +BUG seems like if physical coordinates are assigned, they will become NULL soon. (not sure why..) + + + +2010-03-09 (2) +-------------- +goal: debug pub/sub + +- notifyMapping would store a different Address object for the same nodeID, caused by + the detected port & actual listening port of a service differ. + + + + +2010-03-02 (2) +-------------- +goal: join success + +DEBUG connections seem to terminate abnormally + - found it's because the new message handling method, returning (0) for size would be seen as serious problem + that connection will terminate + +DEBUG packing / unpacking of the Addr object seems incorrect (gets corrupted) when sending RELAY_QUERY message + + + + +2010-02-24 (3) +-------------- +goal: linux build for VAST 0.4.3 + +- different cases for incoming messages + (a) ID request + (b) ID assignment + (c) initial handshake (notification of remote host's ID) + (d) regular message + + we now assume if _remote_ID is not known by net_ace_handler, then + it's either (c) or (a) + +2010-02-09 (2) +-------------- +goal: pub/sub mechanism done + +Class name changes: +- change VASTNode to VASTClient +- change Topology to VASTRelay +- add VASTMatcher + + +2010-01-28 (4) +-------------- +goal: very fast VASTnet layer with topology-aware join & globally unique ID assignment + + +join procedures: + + 1. input a number of entries to VASTnet + 2. connects to a randomly selected entry point + - to get unique ID + - to learn of more relays + 3. concurrent query to relays to find closest relay + 4. connect to closest relay + + +2010-01-26 (2) +-------------- +goal: separate JOIN process into (a) join relay (b) join SPS (a VON network) + so that multiple worlds can be supported on top of a single overlay + + +- The relation between MessageHandler and unique ID is now that: + - each host has a globally unique HostID (currently consists of publicIP + privateIP) + - each "relay-level" host can generate a per-world unique NodeID (HostID + variations) + - each host can have several MessageHandlers, and they are addressable via HostID + - MessageQueue supports both HostID and NodeID as transfer target + +steps: + - allow VASTVerse.isLogin () to determine is_public / obtain physical coord with Topology class + - add getHostID () and getNodeID () to IDGenerator + - move use of IDGenerator.getNodeID () into 2nd stage join + - implement VAST.getPhysicalNeighbors () + - implement VAST.getLogicalNeighbors () + + +2010-02-05 (5) +-------------- +goal: finish join procedure, begin pub/sub + +- for join procedure, some general steps may be the following + - initiate something (like query for closest relay) + - set timeout and wait + - if reponse is gotten, process, initiate more things + - if timeout, re-send request + +- for join, the basic flow is: + 1. get unique ID & determine whether public IP or not (VASTnet) + 2. determine physical coordinate via ping/pong relays (Topology) + 3. query the physically closest relay (Topology) + 4. contact the physically closest relay (Topology) + 5. query for logically closest manager (VASTNode) + 6. contact logically closest manager (VASTNode) + + if the connected relay fails, or a contact request is not responded within timeout, + another request should be sent to a different target + + + +2010-02-04 (4) +-------------- +goal: finish join procedure, begin pub/sub + + +- see that during join there may be two types of routing: + - connect to physically closest "relay" + - connect to logically closest "arbitrator/region manager" + in current design a 'client' connects to physically closest 'relay', but may send/recv pub/sub + messages from its logically closest 'manager'. how to match-up the two efficiently & effectively? + + one possibility is that events / requests are sent to logical managers first, + but updates (bulky messages) are sent via relays back to the subscriber / original requester + + high bandwidth hosts are more suitable as relays + high processing hosts are better as managers + + pub/sub are directed to managers + while message receivals are passed via relays + + +2010-01-21 (4) +-------------- +goal: separate JOIN process into (a) join relay (b) join SPS (a VON network) + so that multiple worlds can be supported on top of a single overlay + +- Redesign login process as follows: + 1. a joiner first contact one to several Relays to + (a) learn of other Relays (if it only knows of 1 initial Relay) + (b) obtain its physical coordinate (by querying these known Relays) + (c) connect with the closest Relay and a few neighboring Relays + + at this point, the joiner knows a few physically close Relays + + 2. a joiner then hooks to a VAST network with a gateway server's hostname, this involves: + (a) request joining and pass authenticationi with the VAST network's gateway server + (b) obtain a unique ID that identifies the joiner within this world + + at this point, the joiner is known as a VASTNode and ready to specify subscriptions and publications + + 3. a joiner is considered joined and can perform one of the followings: + (a) subscribe an area of interest (AOI) at a given Layer + (b) perform point or area publications for messages to a given Layer + (c) receive messages sent to the subscribed area(s) at the given Layers + (d) move an existing subscription to a new location + + + +2010-01-19 (2) +-------------- + +DEBUG: clients would crash when joining a gateway on experiment on PlaneLab + > found out it crashes at call to ACE_OS::gethostbyname () in net_ace's getIPFromHost () + reason may due to missing dynamic link library for using gethostbyname () + +DEBUG: FLoD gateway cannot send message back to a newly joined VAST node + > found out it's because it tries to send the message via sendMessage (), + but target is a VONpeer (so mapping of VONpeer node ID to hostID still maps back + to the gateway itself). + It means that if a discovered ID is a logical VONpeer id, then it must be addressed + and sent via vastnode's send () (see example usage in demo_chatva). + + + +2010-01-18 (1) +-------------- +goal: optimize bandwidth usage (data structure) + +- +BUG: regardless whether timestamp is sent along with MOVE, bandwidth usage is the same + + + +2010-01-15 (5) +-------------- +goal: VAST 0.4.2 release + + +- make sure chat_va compiles okay. both DEBUG & RELEASE mode compiles okay + +- change default simulation environment to (basically a Second Life region x 3) + world 768 x 768 + nodes 30 + velocity 3 + AOI 195 + steps 3000 + overload 10 + +basic sim stat + for 30 nodes, 3000 steps, + we eventually use only 5 arbitrators, 98%+ consistency, 14-16 kb /sec bandwidth use + +2010-01-11 (1) +-------------- +goal: optimize bandwidth usage (data structure) + +- observe that current load balancing method (moving arbitrator boundaries) would make + arbitrators relatively close to each other (stick at center). need to find better load + balancing policies. + + > seems like if arbitrators are more spread out, then it's better + + > seems like if arbitrator size is relatively fixed (instead of allowing dynamic join / leave) + then it's more stable (better consistency) + + > it appears a "come closer" only arbitrator movement request would make all arbitrators to + move close to each other + +- added OVERLOAD_LIMIT parameter to VASTATE's INI file + +MILESTONE: + > allow arbitrators to chase after the center of agents + created desired load balancing effect (region dynamic re-sizing according to load) + + in a 800x600 space, 90 nodes, 1500 steps, eventually only 15 arbitrators are added + with 92-93%+ consistency + but use 37 kb / sec on average bandwidth + + also the movement is too abrupt + + +2009-12-29 (2) +-------------- +goal: remove bad landmark + +BUG: bad landmarks are not reported & removed correctly + + > seems like a terminated program's sockets are still connectable, this also explains + bug 2009-12-28 + > fix by explicitly notify gateway of invalid landmarks to be removed + + +2009-12-28 (1) +-------------- +goal: debug physical coordinate determination + +BUG: for 2nd or above nodes joining the system, "no known relay to contact" error occurs + after repeat login/logoff + + > suspect it's caused by relay missing due to failures, yet the failed relays are not + removed propoerly + + + +2009-12-23 (3) +-------------- +goal: debug physical coordinate determination + +BUG: can build under Release but stuck on Debug mode for the VAST library + +3> Creating library ..\lib\VAST.lib and object ..\lib\VAST.exp +3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) char * __cdecl ACE_OS::strcpy(char *,char const *)" (__imp_?strcpy@ACE_OS@@YAPADPADPBD@Z) +3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) void __cdecl ACE_OS::thr_yield(void)" (__imp_?thr_yield@ACE_OS@@YAXXZ) +3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) char * __cdecl ACE_OS::inet_ntoa(struct in_addr)" (__imp_?inet_ntoa@ACE_OS@@YAPADUin_addr@@@Z) +3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) struct hostent * __cdecl ACE_OS::gethostbyname(char const *)" (__imp_?gethostbyname@ACE_OS@@YAPAUhostent@@PBD@Z) +3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) int __cdecl ACE_OS::hostname(char *,unsigned int)" (__imp_?hostname@ACE_OS@@YAHPADI@Z) +3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) int __cdecl ACE_OS::sleep(class ACE_Time_Value const &)" (__imp_?sleep@ACE_OS@@YAHABVACE_Time_Value@@@Z) +3>common.lib(VASTUtil.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) class ACE_Time_Value __cdecl ACE_OS::gettimeofday(void)" (__imp_?gettimeofday@ACE_OS@@YA?AVACE_Time_Value@@XZ) +3>..\lib\VAST.dll : fatal error LNK1120: 7 unresolved externals + + > found out it's caused by the following line in net_ace.h (took a whole working day to find out this problem!) + + #define ACE_NO_INLINE 1 + + + +2009-12-15 (2) +-------------- +goal: translate latency into physical coordinate + + +BUG + - transmission stat in simulation mode isn't correct (Send < Recv) + + - after destroyNode () is called, if the process holds (waits) then crash occurs + this happens during testing with Actor wrapper + + + +2009-11-29 (7) +-------------- +goal: debug simulation mode + +DEBUG: + - all agents stop moving when new arbitrator is inserted, after some time movements resume + > arbitrator doesn't send updates if its is_owner flag isn't set, howerver, during ownership transfer + the flag is not set at all, so no updates are sent. changed so that updates are not sent only + if I'm not owner, but also the object is not in transit: + (is_owner not set, but ALSO in_transit == 0) + + - new arbitrator join would make existing arbitrator leave immediately + > seems like arbitrator ownership transfer isn't complete successfully when new arbitrator joins + transfer occurs but the receiving arbitrator does not know the object + request for object seems to fail / not occurring + + > problem is caused by the arbitrator uses sendAgent () to send full object update to + neighbor arbitrators, but sendAgent () overrides the msggroup to MSG_GROUP_VASTATE_AGENT + and so the arbitrators would never receive the object states properly + + + + +2009-11-11 (3) +-------------- +goal: priortized messaging + +* debug message processing (remove FIFO receive queue) + - arbitrator departure's VON_BYE isn't properly received by neighbors + > found out it's because the new storeRawMessage () treats messages with size 0 (VON_BYE contains zero field) + as error and drops the message. + + + + +2009-11-09 (1) +-------------- +goal: priortized messaging + +* current problems observed (no cleanup of old object, object reclaim on but no REJOIN message sent to agents) + - arbitrator still claims ghost objects (incorrectly) + - however, agents do not consider them as current arbitrator (existing one not failed) + - therefore arbitrator deletion notice has no effects to outside agents + - extra messages occur for deletion (no message overhead for reclaim) + - currently, consistency under 10 nodes is till 95% + + + + +2009-11-06 (5) +-------------- +goal: correct ownership reclaim & no deletion of self agent object + +* note that JOIN procedure of agent & REJOIN may merge + +current JOIN procedure: +1. agent.join (): VASTnode subscribes to AOI +2. publish JOIN event --> received by current arbitrator +3. current arbitrator + - creates agent object (addAgent) + - notifies agent of arbitratorship (ARBITRATOR) +4. agent adds list of arbitrators (learn of CurrentArbitrator) +5. DONE. agent will send future events to CurrentArbitrator + +current TRANSFER procedure: +1. agent moves into another region (MOVEMENT event) +2. current arbitrator transfer ownership to neighbor (TRANSFER) +3. neighbor arbitrator + - accepts ownership (if object doesn't exist, request) + - create agent object (addAgent) + - notifies agent of arbitratorship (ARBITRATOR) + - send back acknowledge (TRANSFER_ACK) +4. original arbitrator removes its in_transit flag +5. if original arbitratrs in_transit is not removed, reclaim ownership + +current REJOIN procedure: +1. arbitrator counts # of ticks an un-owned object is within its region +2. reclaim directly after time-out +3. sends out REJOIN to agent +4. agent sends out JOIN event to current arbitrator + +** step 3 in REJOIN should not occur (let agent detect and send JOIN event by itself) + + + + +2009-11-04 (3) +-------------- +goal: add back ownership reclaim while keeping correctness / debug crash + +* run successfully 50, 100 nodes under 800x600 for 1000 steps. (50 nodes 2000 steps are also successful) + problems found: + + - arbitrator may delete an owned agent that it doesn't see in its agent list (the avatar object is thus lost) + + - runs very slowly (too much processing / messages) + + - sometimes the partitioning gets weird (doesn't look Voronoi) + + + + + + +2009-10-30 (5) +-------------- +goal: stabalize and correct physical layer VASTATE + + - agents received self deletion message (self disappearance) + > found out it's caused by arbitrator reclaiming ownership for a ghost object (objects it's + no longer receiving updates), and notify the agent that it is its legal arbitrator + (but actually not). The arbitrator then deletes the object (as it's actually outside of + its region, when receiving new update). Thus notifying the agent to perform self deletion. + + current solution is to simply remove the reclaim mechanism.. + + > also added reclaim ownership of in-transit object if acknowledgement is not received in time + + - arbitrators receive exceed EVENTs (continously) + + - JOIN events sometimes fail to receive (50%+ times) + > found out it's because initial subscription area for arbitrator does not cover fully join locations + + + +2009-10-29 (4) +-------------- +goal: physical layer VASTATE 50+ nodes + +potential bug: + - agentLogic's self used / referenced before initialization (by SimPeer, but AgentImpl could already call the onXXX () callbacks + which currently assumes the _self variable is valid (though it may not), causing a crash + + > relocate _self variable from SimAgent to AgentLogic, making it a default function, remove setSelf () function + + - agents may become invalid yet unremoved (possibily due to ownership transfer, or in-transit transfer + agent fail) + however, results of sending updates are not checked against such errors + > add sendAgents () function that checks the success of send results and remove any invalid agents + + - object discovery may occur many times at once (possibly requesting many times after receiving + multiple position updates + > record already sent requests and avoid redundency + + - arbitrator receives many events not belonging to it + > it's normal if it's an agent standing close to the arbitrator (as all enclosing arbitrators receive the events too) + + - many pos_version = 0 are detected at gateway + + +2009-10-27 (2) +-------------- +goal: debug VASTATE real network deploy (should add 30-50+ concurrent) + +BUG spotted: + - crash for agent when arbitrator insertion occurs + - many object discovery suddenly appear at once (not necessarily bug) + - in pure simulation mode, 100 nodes would cause crash (even 50 nodes would have problems, crash on 1500 steps) + - receive msg on deletion of self from arbitrator + +suspected cause: + - arbitrator subscribes to WORLD, instead of its own region only.. + too many publications / VON maintainance for large node size + + - arbitrator insertion point is not good (too frequent partitioning) + what's the optimal join point? + consider arbitrator MOVE? + + +2009-10-23 (5) +-------------- +goal: deploy VASTATE onto Planetlab + +- experimented with inserting arbitrator at mid-way between agent center & arbitrator center + + found that having 10 nodes as threshold is better than 5 nodes (in general) + many instances of agents being notified of self avatar object deletion occurs for 5 node threshold + + but consistency still maintains at over 95%+ if such deletion requests are ignored + + + + +2009-10-19 (1) +-------------- +goal: combine VASTATEsim & VASTsim into one library + +to support: + +1) node creation / movement / destruction +2) stat collections +3) rendering / printing of current state + + + + + +2009-10-20 (3) +-------------- +goal: debug dynamic arbitrator join + + +MILESTONE: dynamic arbitrator join/leave seems okay (up to 30 nodes tested with 95%+ consistency) + +main problems found were: + - suspected was VON stationary departure (but checked it was working, after adding forced notify after depart detection) + - not all arbitrators join as relays (join process simplified) + - join locations were redundent (check for redundency at each selection, also randomize locations) + + +2009-10-07 (3) +-------------- +goal: make dynamic arbitrator join work + +Debug: All of a sudden VASTATEsim doesn't work any more.. cannot even begin simulation, + found out it's caused by addition of VON_EN message, causing message # to exceed 10 and thus overlap with VAST + message #.. (which begin at 10) + + +2009-10-06 (2) +-------------- +goal: debug dynamic arbitrator + +added VON_EN message type, so that when VON_DISCONNECT or VON_BYE is received, the receiver may perform a missing neighbor +check with VON_EN. Note that VON_EN is processed as part of VON_HELLO as well. + + + +2009-09-29 (2) +-------------- +goal: deploy linux self-runnable linux on PlanetLab + +DEBUG: + after setting export LD_LIBRARY_PATH=./ + the following library error still shows: + + ./demo_console: /usr/local/lib/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./libACE-5.6.3.so) + ./demo_console: /lib/libc.so.6: version `GLIBC_2.8' not found (required by ./libACE-5.6.3.so) + + solved by building ACE directly on older build environment (at MMnet server) + +- compile VAST & ACE successfully at MMNET and build a runnable version + (binary built at Plug server cannot be run because the installed libc version in MMNET is older) + + + +2009-09-24 (4) +-------------- +goal: deploy linux self-runnable linux on PlanetLab + + + + +2009-09-07 (1) +-------------- +goal: debug re-join problem (cannot re-join after node fail) in real network + port to Linux system + + + +2009-09-04 (5) +============== +goal: debug VASTATE integrated approach + +- by copying events to also enclosing arbitrators (but only when crossing boundary), we're able + to transit over arbitrator boundary without the 'jumping' in position. Likely because + the event was immediately effective after the ownership transfer. + +- "lean back" is now supported by sending object updates to only the closest enclosing arbitrator + to an object. This way all objects have only ONE backup. However, this approach requires + that arbitrators also have a request-based mechanism similiar to what agents have in + requesting from a neighbor arbitrator the full object states if it receives only partial updates + +MILESTONE: integrated approach works & tested (10 nodes up to 10 arbitrators seem correct) + + + + +2009-08-27 +========== +goal: optimize & debug VASTATE + + +DEBUG solved one self disapperance bug (10 nodes, 5 arbs) + ownership transfer okay, agent also gets new current arbitrator notification. + Problem occurs as there was a previous mapping of the new arbitrator (but to + the incorrect host), due to a previously forwarded QUERY request (as it was + forwarded from gateway, the agent records the arbitrator as map to gateway. + All subsequent events therefore would not reach the new arbitrator. + + Solved by calling notifyMapping () whenever a new ARBITRATOR is received by an agent + +DEBUG solved a potentially nasty bug where agent movement across arbitrators is correct + under DEBUG mode but incorrect (agent stops moving) in RELEASE mode. when + # of arbitrators == 5 (under 10 nodes) + + observe that the agent IDs are different in debug & release. Therefore required + arbitrator be created FIRST before agent is created. then solved the problem + (same agent ID & behavior for both DEBUG / RELEASE mode) + +DEBUG solved another disapperance bug, caused by the new arbitrator not knowing how + to contact a new agent within its region. solved by calling notifyMapping () + + +2009-08-21 +========== +goal: debug self disappearance in multiple arbitrators + +DEBUG: objects near & outside of AOI boundary keep appearing & disappearing + Caused by object deleting occurred while still receiving update publications (so agent would request + the object again). + + Solved by setting two AOI buffer zone, a closer one for notifying agent discovery / disappearance + (onCreate and onDestroy), another further one for actually deleting the object, and a furthest one for + subscriptions. (so with the define VASTATE_BUFFER_RATIO as the zone to remove, VASTATE_BUFFER_RATIO/2 as + the zone to notify agent, and VASTATE_BUFFER_RATIO*2 for the subscription area). + + +2009-08-18 +========== +goal: debug VASTATE attribute update bug in real network + debug self disappearance in multiple arbitrators + + +DEBUG: found a bug of unable to start simulation after 2nd node. + problem was caused by agent trying to join (subscribe AOI & send JOIN event) + before the arbitrator on the same node has joined (yet the arbitrator + manages its region). So this creates a case that the agent's JOIN event was + never processed by a valid arbitrator. + + + +2009-08-12 +========== +goal: test the new "discovery by request" concept + +MILESTONE: +successfully tested "discovery by request" as a valid way for agents to find objects +without arbitrators having to backup more than neighboring arbitrator objects :) + + +2009-08-11 +========== +goal: debug agent-side object discovery bug + +- document two important bug cases in VASTATE-case-2009-08-11 + that includes + 1) inproper discovery due to incorrect arbitrator knowledge + 2) undiscovery caused by AOI spanning across more than two + arbitrators' regions. + + + + +2009-08-09 (7) +-------------- +goal: debug ownership reclaim + +DEBUG + - found out first problem in reclaim is that the node in VASTATE may not have failed properly (added calls to leave () + for both arbitrator & agent when a VASTATE node fails + + - found out another problem is that when ownership for avatar object is reclaimed, the "agent" info is not re-created, + thus the arbitrator may consider the avatar object's agent disconnected and destroyed the avatar object + (addressed by sending ARBITRATOR to the disconnected agent with a flag, after which the agent would send back + the proper TRANSFER to the new arbitrator) + + + + + +2009-08-07 (5) +-------------- +goal: debug ownership reclaim + + + +2009-08-05 (3) +-------------- +goal: implement arbitrator fault-tolerance + +- realize that proper arbitrator fault-tolerance rests on + 1) proper notification of object creation/existence and ownership transfer + 2) assume ownerships of unowned objects in case of neighbor failure + + and it's important to clarify / define behaviors for an arbitrator when + 1) a new neighbor arbitrator is learned + 2) an existing neighbor arbitrator has departed (or become non-enclosing neighbors) + + arbitrators should also notify enclosing neighbors (or all known neighbors?) when + 1) it creates / updates / deletes an object (to all) + 2) it transfers ownership to a neighbor (to target only) + 3) it acknowledges ownership transfer (to target only) + + right now arbitrators process messages only if it's from an agent whose avatar object it owns + (regardless whether the agent is inside its region or not) + + ownership according to region containment can be seen as a 'soft-state' decision + (i.e., ownership is not 100% according to region division, as there could exist temporary inconsistency) + + +- methods to take when + 1) a new arbitrator is learned + a. send current list of owned objects + b. new arbitrator will check against its own and request for any unknown object + c. send new arbitrator missing objects in full + + this will help a new arbitrator to learn fully enclosing neighbors managed objects, + without receiving redundent full object updates for those already known + + 2) a known arbitrator has departed + a. send list of owned objects + b. departed arbitrator will mark those objects as deleted + + + +RELEASE 0.4.0 (file: VAST-c++-0.4.0-src.7z size: 4,479,687) + +This is a major release of VAST, re-adapting the original support of VON +(Voronoi-based Overlay Network) to the support of SPS (Spatial Publish Subscribe). + +Details of VON and SPS can be found in the following papers: + +VON: http://vast.sourceforge.net/docs/pub/2006-hu-VON.pdf +SPS: http://vast.sourceforge.net/docs/pub/2009-MMVE-SPS.pdf + +For other details please see "README.txt" for general descriptions and +"INSTALL.txt" for how to use VAST + + + +2009-08-04 (2) +-------------- +goal: debug 2nd node join bug in chatva, release VAST 0.4.0 + +DEBUG +2009-08-03 chatva 2nd node+ does not move properly (after some pause, 3rd node doesn't move at all), + also cannot display chat messages except 1st node + + found out it's because the VONpeer is initialized with the local host's time (which may be more + advanced), causing subsequent movement updates cannot reflect to the VONpeer by the original Client host. + (unless its logical clock has advanced enough to exceed the Relay's logical clock at the VONpeer's + creation). Solved by initializing VONpeer's logical clock as 0. + + +DEBUG another bug following the above is that chat msg from 2nd node is shown only at 1st node. + found it's caused by adding 'send time' to the PUBLISH message (to measure latency). + However, as when processing MESSAGE, it's sent to local nodes first before remote nodes, + so the sendtime was extracted, and thus the message sent to remote node isn't correct. + Solved by reversing the order to send messages + + +2009-08-03 (1) +-------------- +goal: implement fault tolerance for VASTATE arbitrator + + + +2009-07-30 (4) +-------------- +goal: debug ownership transfer (over 4+ arbitrators) + +DEBUG +2009-07-29 seems like an arbitrator that gets a transferred object would try to delete it at some point + + caused because the agent info is not also transferred, so the new owner does not think + as a valid avatar objects for one of its peers and will consider it as a disconnected agent + (thus removing it) + + transfer agent info as well + + +MILESTONE: ownership transfer under multiple arbitrators success (up to 5) + + did two things to address bugs: + - transfer agent info along with ownership + - upon receiving ownership transfer, also notifies neighbor arbitrators of object creation + (so that subsequent ownership transfer can work correctly) + + + + +2009-07-29 (3) +-------------- +goal: debug ownership transfer + + +- improve the joinRelay () mechanism in VAST. +- agent in VASTATE now also would check for redundent neighbor INSERT (inserting existing, known neighbors) + + +2009-07-27 (1) +-------------- +goal: implement ownership transfer + +- use Visual Leak Detector (VLD) and found memory leakages for VAST, removed some, + but pointers to a the same newed object seem to be counted as leakage even though + the object would be deleted at some point + + + + +2009-07-26 (7) +-------------- +goal: debug update publication & implement ownership transfer + +DEBUG: update publication debugged (07-23 bug) + +2009-07-23 for more than 1 arbitrator (2), the second arbitrator's agents do not seem + to correctly get position updates for agents managed by the 2nd arbitrator + except when just discovering at boundary (then no updates). Appearantly + the arbitrator still updates & keeps track of agent positions correctly, + just that updates are not correctly published / received by agents + (discovery works because it's a one-to-one communication). So seems like + something's wrong with the 2nd arbitrator's publication mechanism. + + later found it's not just 2nd arbitrator, 1st as well. + + found out it's caused by msggroup being filled incorrectly. + for publiation via a vastnode, msggroup should be MSG_GROUP_VAST_RELAY + for updates sent to neighbor arbitrators directly, msggroup sould be MSG_GROUP_VASTATE_ARBITRATOR + the problem occurs when the same message is filled as MSG_GROUP_VASTATE_ARBITRATOR, + but then sent via vastnode (so it cannot be processed by the correct relay). + + re-written the Message.clear () so that msggroup is clear up each time + + + + +2009-07-25 (6) +-------------- +goal: debug arbitrator publication to agents, ownership transfer + + + + +2009-07-24 (5) +-------------- +goal: object & ownership transfer among arbitrators + + +2009-07-24 appearantly the arbitrator & agent in VASTATE would join at different physical coordinates for same host + this seems to be caused by Valaldi giving synthetic coordintes based on host_id (arbitrator & agent have different + host_ids) + + partial fix: valvidi currently assigns the same physical coord to two consequentive hosts (e.g. 1&2, 3&4) + +2009-07-24 relay join cannot succeed for the 3rd node under ENABLE_LATENCY for VASTATEsim + preliminary investigation shows it's caused by the physical coordinate of node 2's agent /arbitrator + has resorted back to the movement coord instead of physical coords given by Valvidi + + found out it's caused by Agent's join () updating the _self's position, which happens to be the + a pointer to the vastnode's position (which should be a physical coord used by the VASTnode to join) + thus corrupting.. this means that getSelf for Agent and Arbitrator should mean different things + (acutally.. both should be the logical coord, while the vastnode's coord is protected from external + corruption). Solved by having separate _self variable maintained within both AgentImpl and ArbitratorImpl + indepedent from the _self variable in vastnode or VONpeer + + + +2009-07-23 (4) +-------------- +goal: object & ownership transfer among arbitrators + + + + + + +2009-07-22 (3) +-------------- +goal: agent can call getArbitrator () and obtain current arbitrator info + +DEBUG: +2009-07-22 certain position updates do not seem to take effect in VASTATE (esp. when the node is at boundary) + + solved by using an AOI-buffer multiplier for the agent's subscription area. + appearantly the bug is caused by the agent not receiving updates sufficiently for + border objects + +- multiple arbitrators join a VON success, can take events and send updates. + however, currently all arbitrators subscribe the same area for events, and create avatar objects + for only those users within their regions (a bug exists that clients do not see other users created + by neighboring arbitrators). + +DEBUG: found out the reason why a 2nd arbitrator was not able to join a VON is because of message + ID collison. the VON messages are in numbers between 1 - 10, so other message handlers + intending to use VON internally and helping to relay VON messages (such as Relay class and now + Arbitrator) should define their message number starting 10 + . + + + + + +2009-07-18 (6) +-------------- +goal: debug peer target not found after node fail + + +DEBUG peer target not found, found out it's caused by the failed node + + - not sending explicit VON_BYE to all existing neighbors + - not sending explicit VON_BYE to potential neighbors (who might have accepted HELLO already) + +DEBUG +2009-06-29 in VAST, some "ghost nodes" sometimes exist when # of node size is large (~50) + + caused by receiving outdated NODE messages + solution: use explicit VON_HELLO acknowledgement (handshake before recongnzing a neighbor) + + +2009-07-09 (4) +-------------- +goal: support debug & integration (known bugs: join may not succeed for 2nd node) + +- add join timeout for VAST node to re-send QUERY message to gateway + + + + +2009-07-08 (3) +-------------- +goal: debug large object creation crash & join not exceeding 3 + +- found out large object creation (100 objects, 1000 attributes, 9 MB of objects created at + a time) or frequent movements do not cause crash, even with ACE. However, if arbitrator + allocates more than 1.7 G RAM (real & virtual) then crash occurs due to memory allocation + error. + + + + + +2009/07/07 (2) +-------------- +goal: debug crash bug when peer disconnects + +DEBUG: solved the long-standing crash bug when an agent leaves, other existing agents + would crash + + found the issue was caused by ACE printing error message about not + being able to re-connect. After removing the invalid debug parameters %p the + problem was solved + +DEBUG: after an agent successfully disconnects, other existing agents would temporily + pause upon movement once in a while. + + it's caused by not clearning VONPeer for the disconnecting agent cleanly, + so existing agents still try to send VON movements to it. + + solved by sending the VON_DISCONNECT properly + + + + + + + +2009/07/01 (3) +-------------- +goal: test multiple arbitrator's JOIN to VON & SUBSCRIBE + +- tested up to 4 nodes in real network (chatva) that shows correct VON behavior + + + + +2009/06/29 (1) +-------------- +goal: debug VAST's consistency issues + +DEBUG: +2009-06-12 in VASTsim neighbor position updates sometimes are lost, or jump abruptly + likely caused by timestamp issues + + found out some small delay in position update is caused by position-only updates + (vs. full update) does not have timestamp, so outdated updates are treated the same + as new updates. Add timestamp to incremental position updates + + another issue is caused by the removeNonOverlapped policy, where the drop count is + checked only every MAX_DROP_COUNT times, and would reset to 0 if the relevance check + passes. So it's possible that a drop count exceeds the limit but the neighbor has + just become irrelevant. This makes too short a time to disconnect irrelevant neighbor. + re-design the check so that an irrelevant neighbor must remain so for MAX_DROP_COUNT + ticks before being removed. + + also found that the AOI_BUFFER setting, should use a resonably large value + (increased from 10 to 20). Also when the receiver of NODE message try to decide + if to accept the notification, it should be more tolerant (use AOI_BUFFER * 2 as + relevance check buffer) + + some simple stats: + + with 10 nodes, + + AOI-buffer Consistency per sec U/L connected neighbor AOI neighbor + 15 99.9620% 3235/3231 + 17 99.9848% 3239/3236 + 20 100.0000% 3246/3242 5.3 ~ 6.3 1.8 ~ 3.8 + + with 50 nodes + 15 99.8485% 9684/9674 + 20 99.9928% 10087/10076 14.4 ~ 16.9 11.3 ~ 12.8 + + + +2009/06/27 (6) +-------------- +goal: debug VASTATEsim (join, move) + + +DEBUG: when gateway replies to LOGIN request by an agent, it cannot find the receiver's ID->address mapping, + as the agent may use a previously existing connection (with unassigned ID). + +NOTE for each SimPeer (in VASTATEsim), two hostIDs are allocated (one for agent, one for arbitrator), + however, ID assignment may occur in any order (agent or arbitrator may obtain unique ID sooner than another) + across different SimPeers + + +BIG BUG: + when agents join VASTATE, they may publish JOIN event to be received by an arbitrator. + However, as agents and arbitrators have different AOI (subscription areas) an agent's AOI + may not contain the arbitrator, and thus will not know the presence of the arbitrator to publish + the JOIN event. + + solution seems to be to support mutual discovery in the VON layer (i.e., A is notified of B not only if + A covers B, but also if B covers A). + +MILESTONE + +VASTATEsim_console seems to be working, after fixing the above Big Bug, by changing the definition of isRelevantNeighbor () +in VONPeer to be of mutual interest instead of just node A's AOI covers node B. +seems like LOGIN, JOIN, and MOVE are working using VASTATE... + +- also try to add cleanConnections () in VAST, but havn't gotten the right way to do it.. + + + + +2009/06/26 (5) +-------------- +goal: debug VASTATEsim (join, move) + + +found out the initial JOIN publication of a joining agent cannot be received by arbitrator +as the agent has not yet subscribed AOI properly (i.e., not a member of the VAST network +to send valid publications). + + + + +2009/06/25 (4) +-------------- +goal: debug VASTATEsim (join, move) + +DEBUG +2009-06-25 calling publish () would crash demo_chatva, detailed trace shows that + using addTarget () inside publish () corrupts the dellocation of Message object + likely issues are caused elsewhere + + found out it's caused by instancing a Message object in chatva and passed into + VAST, when VAST adds a target it allocates memory internally, which cannot + be de-allocated in chatva (outside of VAST). Temp solution: create another + internal copy of the message to manipulate. + + + + + +2009/06/19 (5) +-------------- +goal: debug VASTATE funcions (create/update/destroy objects) + + + +- makes publish () works for VAST (point publication with layer stored as the 'pad' field inside + IPaddr + + + + +2009/06/18 (4) +-------------- +goal: debug VASTATE funcions (create/update/destroy objects) + + +DEBUG: +in VAST when using chatva and only a single relay (client-server mode), when peers leave the +info are still retained. + +found out it's because the VON_DISCONNECT messages have not been properly sent to still +existing peers. Also add the removePeer () function in Relay.cpp + + +2009/06/17 (3) +-------------- +goal: implement VASTATE funcions (create/update/destroy objects) + + +A note on current ID usage / generation + +VASTVerse - maintains host_id +MessageQueue - maintains host_id + +VONPeer - always externally supplied (including gateway) + +VAST - equals host_id, differentiable with msggroup +VASTATE - none (only factory class) + VONPeer - for arbitrators, obtained via getUniqueID (locally generated) + Agent - host_id + msggroup ? + Arbitrator - host_id + msggroup ? + + Agent-VAST - + Arbitrator-VAST - + + + +2009/06/12 (5) +-------------- +goal: debug VAST's real network layer + + +found UDP is causing the disconnection, temporarily use TCP for movmment updates + + + +2009/06/11 (4) +-------------- +goal: debug new VAST architecture (messagehandler) + +* MILESTONE: +VAST works in new architecture. simulation works for 50 nodes in 50 relays or 1 relay. +Can see obviously centralized approach uses slightly less bandwidth.. (as expected :) +real network can connect, but still buggy (okay up to 3 nodes) + +DEBUG: +2009-06-11 non-relay VONPeers do not properly receive neighbor movement updates via their relays + as only a single MOVE is sent if multiple targets exist for the same host, + at the handling side, the same received message is passed to multiple VONPeers several times. + This causes issues in content extraction, where after each pass we need to 'reset' + the current pointer in the content + + +DEBUG: +2009-06-10 no global node position for non-relay nodes + caused by ambiguiy of hostID & VONPeer ID. So when a VONPeer 7 sends a message to VONPeer 2 + via VONPeer 1 (host 1). At host 1, it's unclear whether the message should be handled by + host 1, or host 2. As there can only be one mapping between an ID and a host (id2host mapping) + + solve this issue by separate hostID & VONPeer ID, such that VONPeer ID is now running + in the reserved bits of a hostID (the most significant X bits). this will target to host + mapping would be clear. Also, remove the strict check of destination ID before processing + (now when process messages, a message is locally handled as long as the message is not + for forwarding (no id -> host translation shows a different host) and a local handler + for the message group can be found. This provides flexibility for VONPeer messages + be first sent to a Relay message handler. + + result is when now non-relay nodes also can learn of neighbors, but only at 66% consistency + + + + +2009/06/10 (3) +-------------- +goal: debug new VAST architecture (messagehandler) + +BUG: +- 2009-06-10 normal operations if all nodes are relays, but no global node position for + non-relay nodes & redundent NEIGHBOR messages / notifications (duplicates of neighbors) + probably caused by VONPeer not properly created at a relay + +DEBUG: +join bug caused by + * too short an interval time to determine timely neighbors + (thus a new peer doesn't accept info from VON_NODE message) + * not ticking each VONPeer (during Relay's postHandle ()), therefore + HELLO & handshake are never sent + + +2009/05/15 (5) +-------------- +goal: real network with 30-40 nodes + + +DEBUG: +allow failed node to be removed from neighbor list by recording node's last active time +and remove non-active neighbors after some time (also add sending keepalive MOVE messages +periodically) + + + +2009/05/14 (4) +-------------- +goal: real network with 30-40 nodes + +DEBUG (after node fail simulation ceases to work) +- other nodes still move after node fail, caused by improper handling of DISCONNECT message + (generation and processing of the DISCONNECT message) + + +2009/05/13 (3) +-------------- +goal: debug real network + +MILESTONE: +- real network works for 1st time (3-4 nodes :) + + +2009/05/12 (2) +-------------- +goal: debug real network layer + +- code real network +- send / receive tested with SimNode + + +2009/05/08 (5) +-------------- +goal: debug migration and test on real network + + +2009/05/07 (4) +-------------- +goal: code on peer migration (fault tolerance of relay) + + +- separate MOVE & MOVE_B into MOVE, MOVE_F, MOVE_B, MOVE_FB, where F indicates full AOI info + tested successfully with reduced message overhead + +- make metric collection work (topology consistency, bandwidth, etc.) + +DEBUG: +2009-05-07 send only position update seems to cause topology inconsistency + caused by undefined assignment operator for classes such as Position/Area + so we would update empty positions or AOIs for neighbors + + +2009/05/06 (3) +-------------- +goal: debug relay->client message + code on peer migration (fault tolerance of relay) + + +- correct run of relay -> client messages + + + +2009/05/05 (2) +-------------- +goal: optimize relay -> client messages (position updates) + correct stat messages (consistency / avg. bandwidth) + +BUG: +- notify Client of a Peer's neighbors incrementally (but buggy) + + + +2009/04/30 (4) +-------------- +goal: debug VAST, have VASTsim_gui running + +DEBUG: +VASTBuffer did not initilize its internal buffer size to the allocated one +(which might cause inefficiency in double-allocating every time) + +DEBUG: +the ID request sent from 2nd Client to 1st Gateway does not decode properly. + +found out receiveMessage does not treat the received message as a combined message +(each with individual total_size in front), this is due to how net_ace & net_emu +treat received messages differently (ACE strip out the total_size upon receiving +while net_emu doesn't). + +DEBUG +- for the initial VON_QUERY message, cannot put the 1st relay (gateway) as the contact + address, as the gateway will not have the message handler for the sender node. + solved this by including both the "joiner" and "relay" info in the query message + +MILESTONE: +VASTsim_console runnable at 4:22pm :) +VASTsim_gui working at 5:00pm :) + + + +2009/04/29 (3) +-------------- +goal: VASTsim converted + 1st VASTsim_gui running + +BUG: +serialize & deserialize have different results +DEBUG: +found out it's because after deserialization in Message, the _curr pointer did +not return to 0, so the subsequent calls to extract () were incorrect + + +DEBUG +- add send self messages (route to receive buffer directly) + + + +2009/04/28 (2) +-------------- +goal: VON procedure MOVE + start to run simulation with VASTsim + + +- MOVE done, as well as JOIN, SUBSCRIBE for Peers +- eliminate the Coord class from Movement.h in VASTsim + + + + +2009/04/24 (5) +-------------- +goal: client & relay join cleanup + VON procedures + + + +2009/04/23 (4) +-------------- +goal: client & relay joining + +- initial steps for joining (client contacts VAST node at gateway, which rely with the + closest relay info, client then contacts the relay directly to request join) + + +2009/04/22 (3) +-------------- +goal: creation of Client and Relay nodes + +DEBUG +- serialize / deserialize methods in VASTTypes + virtual functions add 4 bytes to the sizeof () operator to a class, + so inherentance a Serializable class would cause problems when using purely sizeof () and + memcpy (this...) to serialize the class. + + re-implement all serialize (), deserialize () methods for Serializable classes + +IMPORTANT: +- byte-aligned all basic types to 8 bytes, which is VC's default + + +2009/04/21 (2) +-------------- +goal: correct id2host mapping + VAST check whole process, + +- id2host mapping done + + + +2009/04/18 (6) +-------------- +goal: correct id2host mapping + VAST check whole process, + +- work on FLoD-IC draft + + +2009/04/17 (5) +-------------- +goal: VAST check whole process, + Relay, Topology class implementation + +- remove Relay class (realize it can be implemented in the main VAST class + for performing both Client and Relay tasks) + +- unique ID assignment / login procedure done +- find out id2host mapping in MessageQueue needs to be implemented and is important + + + +2009/04/16 (4) +-------------- +goal: unique ID assignment, VAST implementation + +- finished unique ID assignment, +- compiled first VAST library + +DEBUG +- many linker errors at first, resolve by building VASTnet & common as static libraries + then errors occur as MFC libraries are staticially linked in both VASTnet & common + projects. Resolved by linking MFC dynamically as DLL. + Finally resolve linker problems by not using any netemu_bl classes (not converted yet) + + +2009/04/15 (3) +-------------- +goal: VASTnet refactor, + unique ID assignment + + +- try out SVN and backup everything to it (both VAST, FLoD & Plug) +- can compile VASTnet except net_emu_bl + + + +2009/04/13 (1) +-------------- +goal: finish VASTnet refactor + finish VAST modificiation (area subscription, point publication) + +- consolidate various VASTnet classes. compiled most of them now. + + + +2009/04/12 (7) +-------------- +goal: revise VASTnet & message handler mechanism + (support easy logical node implementation & migration) + +- realize nodeID + messageType would be enough for unique handler identification + no need for GroupID or HandlerID as MessageQueue can do the necessary mapping + lookup + +- also connection/disconnection from VASTnet can be removed, just use a notify + function for storing nodeID -> address mapping and timeout for removing + unused connections + + + +2009/04/10 (5) +-------------- +goal: debug & revise VASTnet interface / data structures (compilable) + + +2009/04/09 (4) +-------------- +goal: define all interface and data structures & figure out how they work together + +CHANGE: +separate existing classes into VAST, VASTsim, VASTnet, common libraries + +BUG: when building VASTnet, all files compile but generate the following linker error + +net_ace.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __thiscall ACE_Thread_Mutex::ACE_Thread_Mutex(wchar_t const *,struct ACE_mutexattr_t *)" (__imp_??0ACE_Thread_Mutex@@QAE@PB_WPAUACE_mutexattr_t@@@Z) referenced in function "public: __thiscall Vast::net_ace::net_ace(int)" (??0net_ace@Vast@@QAE@H@Z) +net_ace_handler.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall ACE_Thread_Mutex::ACE_Thread_Mutex(wchar_t const *,struct ACE_mutexattr_t *)" (__imp_??0ACE_Thread_Mutex@@QAE@PB_WPAUACE_mutexattr_t@@@Z) +net_ace.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::init(int,wchar_t * * const)" (?init@ACE_Shared_Object@@UAEHHQAPA_W@Z) +net_ace_handler.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::init(int,wchar_t * * const)" (?init@ACE_Shared_Object@@UAEHHQAPA_W@Z) +net_ace.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::info(wchar_t * *,unsigned int)const " (?info@ACE_Shared_Object@@UBEHPAPA_WI@Z) +net_ace_handler.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::info(wchar_t * *,unsigned int)const " (?info@ACE_Shared_Object@@UBEHPAPA_WI@Z) + +DEBUG: +Found it's because ACE was built with VC7, after building new ACE with VC9 it gets fixed. + + +2009/04/08 (3) +-------------- +goal: define data structures + +- completed half, defined unique ID format, simplify MessageID (remove handlerID concept) + + +2009/04/03 (5) +-------------- +goal: revise VAST paper sections (done 4/6) + integrate VAST with modified networking layer (by Marvin) + + +2009/04/02 (4) +-------------- +goal: modify VAST interface to support SPS + integrate VAST with modified networking layer (by Marvin) + +- modify the new VAST interface in VAST.h + +coding roadmap: + +- modify interface (include network layer) +- integrate network layer + +- three main components + - logical client's VON (join, move, leave, list) VON + - publish support pub/sub + - relay join / modify / leave topology-aware + +- simulation evaluation (we need to simulate a physical network?) + - nodes join / leave (churn) + - movements (sub area moving) + - publish (text chat?) + * should see that subscriber list is up to date, publications are sent correctly + + + +2009/03/11 (3) +-------------- +goal: make cleaned up VAST version compilable + +- done. however, still can't run properly due to some bugs + + +2009/03/04 (3) +-------------- +goal: re-check / refine interface defintion for VAST.h & Network.h + estimate schedule and workload + write down / revise major procedures + + + + +2009/03/03 (2) +-------------- +- restart log for VAST (SPS version) + +goal: re-check / refine interface defintion for VAST.h & Network.h + estimate schedule and workload + + + + +2008/11/22 (6) +---------- +goal: remove IP-lookup from Network. Compile existing functionalities + + +2008/11/21 (5) +---------- +goal: remove IP-lookup in Network interface. Make it compilable with existing + functions + +- see that we need to extract common functionality from net_emu & net_ace + such as send/recv messages + +2008/11/20 (4) +---------- +goal: remove complicated stuff from network interface (simplify!) + +- simplify Network.h interface, compile and fix places where the code breaks + +2008/11/19 (3) +---------- +goal: remove complicated stuff from network interface (simplify!) + + + +2008/10/15 (3) +-------------- +goal: clean up networking layer + fix memory leak bug (VAST consumes continously increasing memory as simulation proceeds) + + +2008/10/14 (2) +-------------- +goal: clean up networking layer + fix memory leak bug (VAST consumes continously increasing memory as simulation proceeds) + +- removing unused codes + +2008/08/27 (3) +-------------- +goal: make better consistency + provide same functionality with peer subscription (manager need not move) + +DEBUG (consistency improvement) +- consistency improves dramatically after the following fixes + * no teleport check (do a JOIN only the first time) + * clear up _new_neighbors every time after it's used + * do not clear entries in _new_neighbors simply due to delay (during bootstrapping some NODE info are late) + * add back 'time' to Node structure. Also timestamp node info when they're sent out by MOVE messages + +DEBUG +- consistency was bad if IP address mapping lookup is used. (better consistency is achieved by including Address within Node structure) + found out it's because when doing insertNode, if no address are provided, a connection should still be attempted. + In other words, regardless of whether IP address is provided, insertNode () should always try to establish connection. + + + + +2008/08/26 (2) +-------------- +goal: debug VAST so VASTsim_gui is runnable + provide same functionality with peer subscription (manager need not move) + + +DEBUG +- found out when deleting an item in a vector or map, *always* record the item index to be deleted in a list first, + then delete in order. DO NOT delete while going through (iterating) the list. + +DEBUG +- if program crash when deleting something, it's possible it's an exported class that gets NEW outside of its DLL, then attempts + are tried to delete it outside of DLL. Such can happen if the exported class doesn't have STL variables such as map or vector + (because they're NEWed inside the DLL when used). Solution: *always* use a factory class for creation / deletion of DLL objects + this is new/delete are always kept inside the DLL + + +- Modify JOIN mechanism. Now only if a 1st time join will a manager contact the gateway. + +DEBUG +- put back 'time' into Node (seems like Node position updates need 'time' in order to be correct) + + +BUG: +- seems like already known nodes to each other would not update each other about its own position correctly (but others are updated). + not sure why.. + + +DEBUG: +- when doing checks for ENs, one should also check if an enclosing neighbor is a "relevant" neighbor (not just AOI neighbor) to + the moving / joining node. (this way we would not miss non-AOI, but enclosing neighbor discovery) + + +DEBUG An important design decision is when nodes connect to new neighbors, how should the remote address be known? +(big) original VON attaches address info along with Node structure, so every neighbor notification tells the network + address. In a later attempt to try to hide as much Address from the overlay as possible, the upper layer only + notifies "which nodes might know the address of which other nodes", and thus the network layer can + query on its own. This somehow can work.. provided such information about "address knowledge" are kept correctly. + + fix the "neighbors do not update each other" bug above, after adding bogus address during insertNode. + reason is that HELLO messages are not able to reach newly discovered neighbors, due to the lack of address + knowledge. + + + + +2008/08/25 (1) +-------------- +goal: VON functionaltiy + Manager can join overlay & move (maintain proper AOI neighbors) + implement: join (), leave (), manage (), subscribe (), move (), getManagers () in new VAST + + +- NOTE: need to make sure when disconnect () in MessageHandler is called, the same physical link is not disconnected until + no handler is interested in the physical link any more (because it's possible more than one handler could utilize + the same physical link) + +- the above is done at 16:20. remove the _net pointer in MessageHandler and allows only MessageQueue has access to network interface. + all connect/disconnect are now handled by MessageQueue where it can keep reference count for each handler that connects to a remote node + only reference count reaches 0 will actual physical disconnection occurs + + + +DEBUG +- got a really strange bug that after new VAST is buildable, building VASTsim (exactly the same code) produces lots of syntax errors. + After spending 40 min. found out it was because there's a new DEFINE in VAST called AOI_BUFFER that replaces the AOI_BUFFER parameter + used in VAST.h + +- finished integrating old VAST code into new VAST, but running GUI still requires debugging. + + + +2008/08/24 (7) +-------------- +goal: implement half of the functions in VAST (new Peer and Manager class) + Manager can join overlay & move (maintain proper AOI neighbors) + +- made MessageHandler & MessageQueue work with virtualized handlers (each node can have several handlers serving manager or peer roles) + +TODO: +- found a fairly serious design flaw / limitation, where the unique id for each node is assumed to coincide with the network layer's node ID. + This has the convenience of being able to use NodeID directly to send / recv messages. However, it creates problems when we want to + support multiple handlers to be used with a network address (IP/port pair). As unique handlerID may consist of more than just nodeID or + networkID. + +- convert existing VAST into new generic overlay VAST.. compilable but VASTsim cannot compile. + + + +2008/08/23 (6) +-------------- +goal: make new VAST interface compilable with modified VASTsim components + implement half of the functions in VAST (new Peer and Manager class) + + +- copy from OGRE the following, to mask the warning about MessageQueue's use of as part of +its private variables + +- add the following +// disable: " needs to have dll-interface to be used by clients' +// Happens on STL member variables which are not public therefore is ok +# pragma warning (disable : 4251) + +- made VASTsim compilable under new VAST functions. + +- add Peer.h and Manager.h class, inherting MessageHandler. Thought of modifying id to accomodate virtual node roles. + But cannot find a uniform way to put different handlers of messages (VAST, Peer, Manager, VSM... ) using a + unified framework. + + + +2008/08/22 (5) +-------------- +goal: make new VAST interface compilable with modified VASTsim components + implement two functions in new VAST + + +- new VAST buildable :) +- introduce new classes + + MessageHandler - can be inherented by any component pluggalbe into the message queue + MessageQueue - represents a unique IP/port interface to network + +- various fixes to make the new library buildable + + + +2008/08/21 (4) +-------------- +goal: make new VAST interface compilable with modified VASTsim components + implement one function in new VAST + +- need a proper messaging mechanism / network abstraction that allows messages be sent & received over the network, + but also usable by different components. i.e., a shared hub for network messages + + + +2008/08/20 (3) +-------------- +- can compile old VAST under VC9 + +DEBUG +- a linker error where .lib for VAST or VASTsim is not found. Solved first by copying old project file. + later found out it's because the WIN32;_WINDOWS macros are not defined + + +2008/08/19 (2) +-------------- +- starts work on generic overlay VAST +- create new project under VC9 for VAST, compilable, also include VASTutil inside VAST, + +BUG: +- VASTsim would be unable to link to some vastverse or VASTutil functions + + + +============================= + + + +2008/03/26 +---------- + +- first try out SVN to get current development from cscsx +- refactor insert_node () so there's only one version (instead of overloaded ones) + + + +2007/12/24 (1) +-------------- +goal: merge movement behavior generator for both FLoDsim & VASTsim + +VASTsim now workable version with new VASTutil behavior & file logger + +DEBUG +- get rid of a crash bug caused by the 'delete' of a map within MovementGenerator + by moving the new and delete into .cpp instead of having them in the .h + (something to do with memory allocation / de-allocation cannot occur accross DLL) + +DEBUG +- found out VASTsim has very poor consistency in 10,000x10,000 world with 500 nodes. + discovered reason is that join is not yet finished for all nodes before simulation + starts. Fix by requiring all nodes pass the is_joined () test before starting + simulations + + +2007/12/22 (6) +-------------- +goal: merge movement behavior generator for both FLoDsim & VASTsim + + + + +2007/05/01 (1) +-------------- + +major DEBUG: +[linux] when demo_gateway is run on Linux, it cannot link back to a U.S. node/host. +- a second cause has been found that in net_ace's disconnect, the mutex for connection object + should not be used + +(potential) BUG: +- VAST's send/recv msg now does not use 'recvtime' but the time received is actually the sender side's + + + +2007/04/30 (1) +-------------- + +major DEBUG: +a strange bug while using FLoD that the server is usable under Win32 but not Linux. +- found out it's due to inconsistant byte alignment of the VAST structures between Win32 & Linux. + So that even the first QUERY message cannot be properly processed. + + + +2007/04/19 (4) +-------------- +- goal: release VAST 0.3.2 + +RELEASE + + +DEBUG: +DD & RS print-out are out of range and very high. Found out later it's because yuli has recorded +and printed out deflated-send/recv and didn't print the captions, yet the deflated-send/recv sizes +do not record correctly if message compression is not used. temporaily disabled printing if +not using the VAST_MC model. + + + + + +2007/04/11 (3) +-------------- + +additions: +- when sending DISCONNECT should use timestamp = 0 in net_emu +- when doing sendmsg () or flush () in net, should return actual # of bytes sent instead of supposed bytes + + + +2007/03/31 (6) +-------------- +- ACE 5.5 built successful on linux, after failing manual build, downloaded pre-built + RPM at http://dist.bonsai.com/ken/ace_tao_rpm/ + + then build vast-c++-0.3.1 successful with ACE + + +2007/03/22 (4) +-------------- + +MAJOR CHANGE +- add 'vastbuf' class and used for all send/recv buffer within VAST (so that message of + any size can be sent without buffer overflow). However, this does not apply to + UDP messages + + +2007/03/15 (4) +-------------- +- hide VASTID from overly exposing (so that other libraries won't have to link to VAST + all the time) + +DEBUG: + +found out chatva client not responding is due to: +1. when gateway replies ID it's not using reliable delivery +2. chatva does not run tick () when ID is not yet obtained, therefore it never processes + incoming messages + +* chatva runs succesfully to up to about 40 nodes (starts to fail joining) in Released mode + but CPU utiliziation is around 80% and seems like the problem might be caused by the + local host not being able to handle it... + + + + +2007/03/10 (6) +-------------- +- goal: write visual diagnostic for voronoi construction (large data-set verification) + +DEBUG: +found out topology consistency decreases dramatically for VASTsim in the new model. +discover the cause as logical time not advancing (vastverse is not tick'ed) and nodes +do not process messages as nodes join (so too many join request may accumulate). Solve +this by adding back the node processing. however this effectively decrease the +data collection. + + + +2007/02/09 (5) +-------------- +- goal: finish adding new materials to NOSSDAV draft + +paper additions: + +- add considerations for +- smooth out paper + + + + + +2007/01/30 (2) +-------------- +- goal: results, convert to Latex + +DEBUG: peers have very large IDs + cause: VASTATE is not ticked when peer joins in the beginning.. causing many IDs to be + assigned to the same peer + +VASTATE +BUG +- peer receives message deleting its own avatar object +- arbitrator thinks it is owner, then delete a peer object, notifies others (so no one now has it) + yet the peer still wishes to connect... + +- notices that after a long pause at one step, many pos_version = 0, then crashes... + + +what would be some of my own questions? for VSP..? + +failure + - what happens when node fail ? arbitrator fail.. can you recover? under what condition? + how much cost? how effective is it? + +load balance + - will nodes really distribute load? how much cost? how effective is it? + - load of the server/arbitrator, initially? later? + +scalability +- can your system scale? +- what are some bottleneck points? in what situation.. + +bootstrapping + - how is server inserted? + - how much arbitrator capacity is required? + +others + - can an event / object becomes invalid? + - how does node capability percentage/distribution affect loading/load balancing? + + +Overload/Underload threshold +27, 66, 191kb for min, avg, max during randomwalk test (600x600, with 100, 200 & 400 people) + 11, 22, 44 / region (out of 9) + 46.5 + + 50 & 130kb + underload overload + + + +2007/01/29 (1) +-------------- +- goal: finish whole draft +- done: procedures, ver 1-0. + +VASTATE bug: +- ownership received but object doesn't exist.. + +FUTURE WORK: +- for boundary arbitrators, they could backup to nodes that'll never be able to take-over + (thus wasting storage...) + + + + +2007/01/28 (7) +-------------- +- goal: finish draft (procedure - morning, evaluation - afternoon, intro & background - evening) +- done: psuedo code + +VASTATE bug note: + +- a peer sends three movements + one grab event of food to arbitrator (all delayed to be executed + in the same run), event may not be forwarded to the enclosing neighbor as forwarding + was based on current location. if the 'food' is in another region, the region owner may not + have received & executed the event + +- when arbitrator updates its list of enclosing arbitrators (EAs) and find that a node is no longer the + EA, it would send DELETE to that EA for objects it owns. This currently works as VAST does not + disconnect right away non-EA nodes, and the EA judgment is done internally on a separate Voronoi + by the arbitrator. However, in real situations, it's possible the EA link is already destroyed by + VAST, making DELETE unable to reach the EA, causing zombie objects. + + + + +2007/01/26 (5) +-------------- + +- VASTATE: events that create object may be processed by all enclosing arbitrators + -> multiple ownerships + + + - peer if join fail, then need to re-join via Gateway + - send 'tick' for objects, record last updated event, remove aged zombie objects + - rockets should create its own event by arbitrator + + +new arbitrator notification should send along pending events + + + +2007/01/17 (3) +-------------- +- goal: finish VASTATE implementation + +- LEAVE handled for both peers & arbitrators (fault-tolerance) +- arbitrator promotion/demotion requests mechanism changed (to querying the VASTATE interface) +- AOI adjustment for arbitrators + + +2007/01/16 (2) +-------------- +- goal: finish VASTATE implementation + + +- enable new ID scheme in VASTATE (compilable) +- add cleanup mechanism to net_emu in VAST + (when stop() is called, all connections are dropped/disconnected, + also, message queue is freed up in destructor) + + +2007/01/15 (1) +-------------- +- goal: finish VASTATE implementation + +- "chatva" now workable with the new VAST (separated ID assignment) + +- VASTATE modified to suit the new VAST (compilable), but initial ID assignment not yet integrated + +DEBUG +- add critical sections to various access to the network connections in net_ace to avoid thread + contentions of the connection (sending while deleting) + + + +2007/01/10 (3) +-------------- +- goal: add LEAVE procedure + modify network.h to add message handler hooks & subsequent user behaviors (VAST, VASTATE, FLoD) + + + + +2007/01/09 (2) +-------------- +- goal: debug the remove non-AOI objects for arbitrator + +DEBUG +- discover that the bug was caused by non-AOI objects' positions are no longer sent to the + arbitrators that cannot see them, the positions were there still out-dated and will not be + removed by the remote arbitrators' removal mechanism. Fix the problem by having the sending + arbitrator makes all the decisions (including whether to remove non-visible AOI-objects, by + sending a OBJECT update with pos_version = 0). + + + +2007/01/03 (3) +-------------- +- goal: implement LEAVE mechanism in VASTATE + + + +2007/01/02 (2) +-------------- + +DEBUG +- discover a bug in net_ace_handler attempt to delete a UDP pointer to an + ACE_SOCK_Dgram object, which was actually supplied by net_ace. So modify the UDP socket creation + process and create the openUDP () for net_ace_handler. Tested with 3D streaming app + where program exit no longer crashes + +- another crash bug when terminating, is when deleting a vast node under VASTsim. + cause is that the 'network' interface for some nodes are already removed, yet other nodes would + still attempt to use them in order to call remote_disconnect (). Simple fix by unregistring + the network object from bridge when deleting. + +- increase buffer size from 4096 to 40960 as if a TCP message exceeds 4096 the program will simply + crash + + +2006/10/03 (2) +-------------- +- goal: add 'storage' interface to VASTATE (allow query for stored data item without necessarily using C/S or distributed + query/delivery). + + + + +2006/09/27 (3) +-------------- +- goal: debug VASTATE (remove non-AOI crash bug & consistency decrease) + +- remove non-AOI objects for arbitrators now no longer crashes (as now all objects are created within + an arbitrator's AOI. however, removal would decrease the update consistency for states. Also, + discovery consistency is not 100% some of the times, reasons still unknown. + + + + + +2006/09/26 (2) +-------------- +- goal: debug VASTATE (remove non-AOI crash bug & consistency decrease) + + +BUG (big): + 1) in VAST, a boundary neighbor may not properly notify for new neighbor discovery, if it considers + the moving node and the new neighbor as enclosing neighbors of each other, when in fact they + aren't (this is due to the boundary neighbor not knowing another more distant neighbor that + prevents the moving node and the new neighbor to be mutually enclosing.) + + soluions? (connect beyond EN set when connection limit is not yet reached? in other words, + dynamically increase AOI-radius when CN is not reached, and adjust back to maintain fixed + number of connected neighbors?) + + +2006/09/20 (3) +-------------- +- goal: debug VASTATE, add arbitrator object management (removing non-AOI objects periodically) + + +- add 'beh_clustered' to the stock of behavior_models + + + +2006/09/19 (2) +-------------- +- goal: debug VASTATE + +DEBUG: + 1) found a bug where the arbitrator notifies a peer of new objects (using extended AOI-radius) + but the peer immediately removes it by using only its original radius to check + (object undiscovery therefore occurs) + + - solve this problem by dividing the client object discovery process into two parts: + storage & notification. where a notification occurs only if the object comes into AOI view. + but will only be remove from object store if it leaves an buffered AOI. update consistency + has come to 100% after 3 steps in most cases. + +- convert project file to .net2003 (all projects compilable) + + +2006/09/13 (3) +-------------- +- goal: debug VASTATE + +DEBUG: + 1) an annoying bug where many times, the program would crash within sfvoronoi, where doing + a free (buf_list[]) in calvs() tries to free invalid buf_list. + + the bug (found by ¤Öºd) was that in processmsg of VAST, we'd collect stat info of msgtype + via + + _msg_stat[msgtype] += size; + + But after VASTATE is added (and it uses msg number starting from 100), it access + unallocated memory in _msg_stat, causing data in Voronoi to corrupt. + + A fairly easy fix, but difficult-to-trace problem. + + after this is solved, not only voronoi works properly now, a previous "unable to run under + Release mode" problem is also solved. + + +_msg_stat[msgtype] += size; + +2006/09/09 (6) +-------------- +- goal: debug VASTATE + +DEBUG: + 1) after updating interests using not just owned objects, but all known objects + (but do not use arbitrators as destinations), the program would mysteriously crash + upon init. Trace reveals that the map '_interested' contains NULL items + (that is, a 'find' would get something, but the value is 0, or NULL). + suspects that it's because of an access such as + + notify_peers (_interested[info.obj_id], STATE, msg, size); + + although no value is assigned to _interested, by accessing it using [] operator, + a NULL value is put into the map + + * confirmed the hypothesis and solved the bug at 18:00 + +BUG: + 1) small bug, if a node doesn't move, it won't be made known to a peer joined later + (even if it's in the AOI of the late peer). Initial object notification wasn't done properly? + + +2006/09/08 (5) +-------------- +- goal: debug VASTATE + +BUG: + 1) neighbor undiscovery across regions (peers cannot learn of AOI neighbors in another region) + 2) removal of non-AOI objects + +MAJOR CHANGE: + revise directory structure, now into the following: + /bin - binaries + /demo - demo files (chatva, gateway) + /errout - error_out + /include - shared include (VAST, VASTATE, VASTsim, VASTATEsim) + /lib - libraries (unused) + /sim - simulations + /test - test files + /VAST - VAST files + /VASTATE - VASTATE files + +Projects have also changed names into + errout + VAST + VASTATE + VASTATEsim + VASTATEsim_gui + VASTsim + VASTsim_console + VASTsim_gui + + + +2006/09/06 (3) +-------------- +- goal: debug VASTATE + +DEBUG: + Another bug found where the peer does not remove non-AOI objects is that: the arbitrators immediately + stops sending updated position to a peer if the objects become non-AOI, however, the peer thus + still retains old knowledge and considers the objects to be AOI-objects. So the peer does not + remove the non-AOI object. + + + +2006/09/05 (2) +-------------- +- goal: debug VASTATE + +DEBUG: +'_peers' was not updated after initial creation, solves the bug where some nodes stop moving after a while.. +(because they were seen as 'not interested' in the object updates, due to their positions were + never updated after initialization) + + +2006/09/01 (5) +-------------- +- goal: debug VASTATE + +BUG: + 1. system consistency in 4 steps would drop after simulation has run for a while + 2. object attribute version would still increase even if they were not modified. + + + + +2006/08/17 (4) +-------------- +- goal: debug the peer not moving issue + +DEBUG + solved the issue of peers not moving when crossing arbitrator regions. + found the issue were + 1) the peer did not learn properly the arbitrator list when entering a new region + 2) region transfer check was not called by peer + + + +2006/08/12 (6) +-------------- +- goal: finish first implementation of VASTATE (load - balancing) + +- finished first completed implementation of VASTATE + + +2006/08/11 (5) +-------------- +- goal: finish first implementation of VASTATE (load - balancing) + + + +2006/08/09 (3) +-------------- +- goal: add n-tier neighbor to Voronoi class + finish first implementation of VASTATE (load - balancing) + +- add a 'level' parameter to the Voronoi class's get_en (), so that which levels of ENs may be + specified. + +MAJOR CHANGE: +- change the internal data representation of a "Position" class from 'long' to 'double' + + + +2006/07/28 (5) +-------------- +- goal: finish first implementation of VASTATE (load - balancing) + +DEBUG +- solve the event ordering issue by revising net_emu, where messages are now handled by +a multimap with timestep as index, as opposed to the linklist. + +DEBUG + be careful of using the "[] = NULL" to check for something that's not in the map, + it could very well be that something exists but returns NULL (0) nevertheless.. + +succesfull make event forwarding to work, however, found out that if two foreign nodes +both try to use their previous values to update a new value, then the effect of +one operation would be cancelled. For example, if node 1 owns A, with value 7. + +Then in one step node 2, 3 all try to increment.. with the knowledge that A = 7. +Then both would send in the request of A = 8 to node 1, where after applying both +updates, the result will be A = 8. However, it might sometimes be desirable the result +is A = 9 (applying two +1 operations). + +But at least updates are displayed and notifed consistently across nodes. + + + +2006/07/27 (4) +-------------- +- goal: finish first implementation of VASTATE (ownership check/transfer & load-balacning mechanism) + +finish 1st working ver of owner transfer check +still has small problem about event ordering (inserting a timestep=0 event would not come +in the beginning of message queue) + + + +2006/07/26 (3) +-------------- +- goal: finish first implementation of VASTATE (ownership check/transfer & load-balacning mechanism) + +The process: + - send in command (an update to a particular/specified object) + - generate bytestring + - deliver to event handler (local or remote) + - execute the request after checking ownership (owned: execute, un-owned: forward to owner) + + + + +2006/07/25 (2) +-------------- +- goal: finish first implementation of VASTATE + +DEBUG + solved an annoying bug where the encode/decode of messages seem to have problems + (decoding incorrect bytestring). found out after much inspection that the problem + was in for loop, the 3rd paremter will actually execute/validate first before seeing + if the 2nd parameter will run.. and I had an pointer advancing like this + + for (int i=0; i 150 steps) some nodes would keep large number of + neighbors (25 in a 30 node simulation, for example). When trying + to trace in Debug mode, it would crash after steps 23 - 25. + However, if using releae mode then it wouldn't crash. + + +2005/08/28 (7) +-------------- +- goal: improve dAOI consistency + + +2005/08/27 (6) +-------------- +- goal: improve consistency while saving bandwidth + + +UNDISCOVERY (simucase-10) + a new node may not be discovered in time because eligible BNs that are + able to notify are also just learned by the moving node. + + solution: increase the detection radius (BUFFER_MULTIPLIER increased to 3) + correction: it's not necessary to increase BUFFER_MULTIPLIER, + all we need to do is to +1 in on the AOI-radius while + doing neighbor discovery check (!) + +Ver 0.2.8 (collect stat after stablized) +------------- +DEBUG fix a small initialization bug in GUI by adding a bit displacement + when doing initial join (helps to ensure system will stablize) + (there's a position set that would make GUI pause for a long time + waiting for stabliziation) + +BUG found out for the same set of data, the crashset would produce + inconsistency at a very late step (reason unknown) while + if the fullset is run (1000 steps) then no inconsistency would + occur + +DEBUG found out that if the simulation runs without first doing + displacment-based stablization, then consistency would be good. + (weird thing described in previous BUG doesn't happen) + decided to not run psuedo-stablization steps but run + simulation directly. only to reset stat counters after + reaching first 100% consistency. + + +2005/08/26 (5) +-------------- +- goal: improve consistency while saving bandwidth + +- initial runs of v0.2.7 shows that consistency has improved, however, + inconsistency still occurs for nodes above 400 + (400 - 99.9994%) + (500 - 99.9693%) + (600 - 100.0000%) + + it was quite frustrating until I check out where does the inconsistency + occurs -- at the beginning of the simulations at time-step 11 + (beyond step 10, the data collection point) + + solution: try to calculate/collect stat only after the system has + stablized to the first 100% point. also collect # of time-steps to + reach stable state. (done!) + +- another observation is that bandwidth use under v0.2.7 increases + exponentially. % of NODE message repeats confirms this, where it rises + fairly high between 70% - 90% of all NODE messages as # of nodes increases. + +- print inconsistent nodes within logfile + + +2005/08/25 (4) +-------------- +- goal: improve consistency while saving bandwidth + +- some stats: + + check all: all known neighbors are checked for new overlap or new EN + check EN: only EN are used for neighbor discovery check + no clear: does not clear out _neighbor_states after MOVE is received + +Simulation Parameters +nodes: 50 world: (600, 600) steps: 1000 +aoi: 100 connlimit: 0 velocity: 5 lossrate: 0 failrate: 0 + + Neighbor discovery methods + -------------------------- + check all check EN check all check EN + (no clear) (no clear) +---------------------------------------------------------------- +max_drift 15 10 10 10 +consistency 100.0000% 99.9996% 100.0000% 99.9996% +avg. trans 2671 2398 2436 2278 +max. trans 11431 8749 8874 8023 +NODE repeat % 0.749 0.775 0.664 0.726 +NODE repeat # 2643 1939 1714 1468 + + +Ver 0.2.6 (batch processing of NODE messages) +------------- +DEBUG found out when processing NODE message, a node may be judged if + it's relevant on a per node (message) basis. However, this could + be slightly incorrect if not all new node positions are considered. + therefore we should make the judgement only after updating the + most recent/correct position of all moving neighbors. + + fix this problem by recording all NODE messages first and then + do connection decision in a batch. consistency has improved slightly. + +Ver 0.2.7 (time check) +------------- +DEBUG improve drift distance (and hopefully consistency as well) + by ensuring each NODE message contains a timestamp, and only + the more recent timestamped message would be used during + notification. + + add parameter 'time' into Node data struct in typedef.h + also changes aoi_t from 'long' to 'short' (so that a Node + structure remains to be 16 bytes) + +2005/08/24 (3) +-------------- +- goal: improve consistency while saving bandwidth + +- some testing shows that about 75% of NODE messages were redundent + (notifying for nodes already known). And more redundent messages exist + when all neighbors are being checked than just ENs (this explains the + surge of bandwidth use). Certainly an area for improvement. + + some ideas are: + - when a node beings to send MOVE instead of MOVE_B, this clears the + neighbor_states list, however this will cause much redundent NODE + messages be sent when the moving node again sends out MOVE_B + so overlap & EN checks should always be performed (but sends NODE + only when necessary) + + - try to see if there are deterministic methods to separate NODE + message streams (that is, among the notifying nodes, try not to + send redundent NODE messages) + + +2005/08/18 (4) +-------------- +- goal: write 1. related works 2. DT-overlay + +DEBUG the crash bug from the new fail/join mechanism is caused + by accessing _neighbor_states during the neighbor discovery + stage. _neighbor_states becomes invalid because right now + neighbor discovery is done in batch, so there could be + DISCONNECT messages in between MOVE_B. Add a check to prevent + invalid access of _neighbor_states. + +- add a small bandwidth saver: + store the info sent by EN message as the initial data in + _neighbor_states (assumed that the sender considered those nodes in EN as + overlapped). this saves about 5% bandwidth use. + +- found out that when doing neighbor discovery check, using only the + EN and all neighbors for the check have different consequence: + using EN only saves bandwidth but has lower consistency, while using + all neighbors usually give very good consistency but uses at least 10% + more bandwidth + +2005/08/17 (3) +-------------- +- goal: implement node fail/join mechanism + +- did a version that implements join/fail, by specifying a FAIL_RATE, + then within VASTsim library's NextStep () it would randomly make a + node leave the overlay, then re-join with a new position. Also modify + vast_dc accordingly to prevent re-obtaining node id. compiled alright + but would crash when run. + +2005/08/16 (2) +-------------- +- goal: achieve 100% consistency for basic & dAOI model + +DEBUG +- investigate the seemingly large drift distance. found out part of the + problem is that when a node is first learnt, its position might not be + the most up-to-date, therefore it would have some non-zero drift. + however, the situation is usually resolved after the node has moved + into the AOI. Therefore, calcuation of drift distance should be + modified to only that within AOI. + +Thoughts on VAST 1.0 +-------------------- + +Two new developers will be joining the developments of VAST: +Chang Chen Ye-Zen and Yakko. Both were friends I met back while at +the Computing Center of NSYSU, and have been professional software +developers. + +There were some discussions over the weekend on what we should do next, +and Yakko suggested to make a To-Do list for perhaps VAST version 1.0 -- +a practically useable version. So here are some initial thoughts organized +into three categories: +(you're welcome to contribute ideas to add or modify the list) + + +Research +-------- +- Latency tolerance mechanism (consistency under packet delay & loss) + + packet delay and loss are inevitable on real networks, therefore + techniques are needed to deal with latency, jitter, and packet loss. + 100% consistency in "logical time" can be maintained if we can wait + indefinitely for retransmission of late or lost packets. however, + "real-time" requirement usually does not allow this. + + +Design +------ +- Data format and protocol specification. + + VAST might be adopted more widely if a set of common protocol and data + formats are developed. + + +Implementation +-------------- +- 100% topology consistency and low drift under 0% packet loss, + and "good enough" performance (for example, above 95% consistency) when + it is acceptable for some packets to be lost. + + The first scenario is useful when real-time response is not strictly required + (for example, when we're running a simulation without real people involved, + but accuracy of node positions is required), + and the second is usually the case for applications that + involve real human avatars (when we cannot wait for delayed packets indefinitely) + + This characteristic will need to be tested by simulation. + +- NAT-transversal. + VAST should be usable for people who're behind VPNs. + +- Cross-platform network transmission. + delivery of reliable and best-effort data should be supported. + (that is, regular and reliable UDP delivery) + + +2005/08/15 (1) +-------------- +- goal: achieve 100% consistency for basic model + +- found out the addition of sending DISCONNECT message would cost + much bandwidth (20% more bandwidth to obtain slight increase in + consistency). Experiment with an alternative approach: + do not send DISCONNECT message, but notify the moving node if + a node becomes overlapping new neighbor, regardless of whether it was + previously notified as an EN. This will preserve much of + the original mechanism, and there will be no DISCONNECT message + to send. this approach increases traffic by about 5% than the OVERLAP_MULTIPLIER=4 + approach for 50 nodes in 600x600 environment. + + following is a list of comparison of various approaches: + (simulation parameter: 600x600 50 nodes, 1000 steps) + + Approach Consistency Avg. Trans. Max Trans. (bytes/second) + ---------------------------------------------------------------------- + original 99.9996% 2700 8858 + (overlap multiplier=4) + ---------------------------------------------------------------------- + DISCONNECT 100.0000% 3269 11444 + (no NODE_EN) + ---------------------------------------------------------------------- + DISCONNECT 100.0000% 3281 11520 + (NODE_EN) + ---------------------------------------------------------------------- + no DISCONNECT 100.0000% 2878 10512 + (separately notify) + + +2005/08/14 (7) +-------------- +- goal: try to solve the less than 100% consistency problem with + OVERLAP_MULTIPLIER set to 2 (instead of 4) + +DEBUG achieved 100% consistency in test run after adding + DISCONNECT message to include when a node refuses to connect + to a NODE message (notify the BN that the NODE message was + not processed, so that it may has a more correct view of + "known neighbors" for the mover) + + +- fix a small bug to exclude calcuation of internally generated + DISCONNECT message (this causes higher amount of RECV traffics than + actual, avg SEND and RECV should be the same amount when it's 0 packet loss) + +- + + +2005/08/12 (5) +-------------- +- goal: try to solve the less than 100% consistency problem with + OVERLAP_MULTIPLIER set to 2 (instead of 4) + +- found out when to call remove_nonoverlapped () is a tricky decision. + currently it is done immediately when setpos () is called. + however, there are two other places where it might be placed: + 1. at the end of setpos () + 2. at the end of processmsg () + + however, the other two possibilities generate lower consistency than + when it's called right when setpos is called (reason unknown) + + +BUG found out a troubling issue related to the algorithm itself: +(big) nodes may have different judgements about enclosing neighbors, + which causes missing neighbor discovery. for example: + + it is possible that node A may disconnect node B because + node B fails both the overlap and enclosing neighbor test. + however, a middle node C may still consider node B to be node A's EN + (due to the fact that node C and node A have different known nodes) + therefore, to node C, node A should know node B (that it is unaware + of the disconnection between A & B) + + two possible solutions: + 1. node A also notifies node C when it disconnects node B + 2. node C does not keep enclosing neighbors as part of the + "known-node" list, thus would continously notify for new EN, + and would also notify when two nodes become overlapped. + + after some testing, solution 2 does increase consistency, + at the cost of more traffics (an increase from 200% to 10%, + depending on node density) + +BUG after applying solution #2 above, there's still some inconsistency. + Tracing shows that it's because due to the way behavior model + generates positions, a node may record its own maximum speed + as somewhat less than actual (for example, 4 instead of 5). + This would cause misjudgement when this node is doing + neighbor discovery for others. If it uses other nodes's speed + as max_velocity, there'd also be problem if a movement packet + is skipped (due to unknown reason, possibily a reconnect followed + by disconnection?) and causing max_velocity to be + greater than actual. + + solution: specify max_velocity when creating a vast node + instead of calculating from position records + (not adopted, still use recorded results, in order to maintain + the separation of functionality) + +NOTE there's another situation that needs to be considered, which is + when Node1 notifies Node2 for a new neighbor Node3, it would + subsequently assume that Node 2 will be aware of Node3. However, + in reality, Node2 would judge independently whether to make the + connection, therefore Node1's knowledge could be wrong and + does not notify again when it should. However, analyzing further, + this shouldn't happen for overlap test (as it is universal) + but only would happen for enclosing neighbor test, so perhaps + solution 2 above is a better choice, though it might generate + more traffics + +NOTE: an important observation is that, for neighbor discovery + notification to properly function, it's quite important that the + "buffer area" is correct. Buffer area is ideally the combined + distance two potentially visible nodes travel to each other. + However, if the buffer area is specified, we will need assumption + about maximum velocity of all nodes. If it's too large then + connections could be wasted, if it's too small then undiscovery + could occur. + +BUG another subtle bug: because during neighbor discovery check, only + ENs are involved. therefore if a new neighbor + that should be learned is not the EN of the checking node, then + neighbor discovery could miss until other mechanisms set in + (for example, both A & B ask C to check for new neighbor, + A is C's EN but not B, therefore B would be notified of A but + A isn't notified of B. A will learn of B when B contacts A, + or when B becomes some other nodes' EN which A requests new + neighbor check) + + solution: one simple solution is to check for new + neighbor discovery from *ALL* known nodes instead of just the EN set + (adopted) + + +2005/08/11 (4) +-------------- +- goal: try to solve the less than 100% consistency problem with + OVERLAP_MULTIPLIER set to 2 (instead of 4) + +- organize trace messages + +DEBUG: found out overlap check would miss because _max_velocity was 4 + (instead of the correct 5) as it was actually re-calculated + every time-step. the problem seems to disappear (for node=30) + after making sure _max_velocity was indeed 5 + + +2005/07/05 (2) +-------------- +- goal: put first initial release of VAST (v0.1) + (but must be relatively stable under emulator mode and + can generate all results in the paper without problems) + add packet_loss parameter + + +2005/07/04 (1) +-------------- +- did some testing & experiments, found out + + using a OVERLAP_RADIUS_MULTIPLIER of 4 can almost guarantee 100% consistency + even with velocity of 10, though 3 already works well for velocity of 5 + + however, if NODE messages are processed in the same time-step as when the + neighbor discovery is made (instead of the next step), using a multiplier of + 2 is enough (vel=10). + + This supports the hypothesis that the multiplier basically + serve as a counter-measure to latency. + + + +2005/07/02 (6) +-------------- +- attempt to increase consistency while decrasing gap between + CN & AN. + +- tried to fall back to original overlap check method and see if + consistency can be maintained without using OVERLAP_RADIUS_MULTIPLIER + + had first series of simulation results.. CN increases while consistency + is maintained, though more inconsistent in dAOI model. + +2005/06/16 (4) +-------------- +- goal: get new version to work on linux & start generating new result sets + for IEEE Network paper. + +DEBUG on linux the behavior model seems to generate non-random movements + (initial positions for many steps do not change at all) + appearantly every time the program calls the move_all(), + random number are reset according to current time. + + solved by keeping a _last_seed to reseed random number generator + every time. This brings movements to be much more random. + + however, for the same set of positions, linux and windows + generate slightly different results (reason unknown) + + + +2005/06/15 (3) +-------------- +- goal: debug dAOI inconsistency problem (20:30) + +Simulation Parameters for below comments + nodes: 30 world: (600, 600) steps: 990 + aoi: 200 connlimit: 13 velocity: 10 lossrate: 0 + +- experiment with various multiplier when doing nonoverlap test. + found out 1x _max_velocity works best (99.88% consistency) + with more multiplier causing decreasing consistency. + however, lowest consistency is experiecned (99.78%) when not + using any multiplier. + +- even better consistency (99.9155%) is achieved if no multiplier is + used to check if the node to be disconnected has larger AOI to cover me + (reduce from 4x _max_velocity to 0 or 1), + however AOI-radius is also reduced dramatically. + +- after some trials, appearant a BUFFER_MULTIPLIER of 4 will produce + 100% consistency in basic model. However, for dAOI model, using + a BUFFER_MULTIPLIER of 4 but when doing remove_nonoverlap() use only + a multplier of 1 seems to achieve better consistency. + likely reason is that in dAOI it's necessary/preferable to have + connections be disconnected as quickly as possible if no longer + relevant (otherwise will take up valuable connection resource) + +DEBUG min_aoi in some cases is 0 or smaller than 0 when running in +(big) dAOI mode. found out it was because aoi_t was set to "unsigned long" + which did not allow for negative numbers. However, when shrinking + AOI it's possible to get negative, which in turn becomes a very + large positive number. therefore the <= 5 test would not reveal problem. + + fixed by changing "unsigned long" to "long" + +MILESTONE + achieves close to 100% consistency for dAOI after accepting all + remote connections and do not disconnect unless both nodes are + mutually unaware, specifically the following modifications are made: + (recorded as simu-cases "undiscovery 6") + + - removes the HELLO_E to accept all incoming HELLO + - send out OVERCAP when trying to disconnect a remote node that + still considers me as its AOI neighbor + - drop the use of _dist_drop (count_drop was enough) + +2005/06/14 (2) +-------------- +- goal: debug dAOI inconsistency problem + +DEBUG: a rare undiscovery problem is caused by the remote node's + notification of a new neighbor. Yet the node that was + notified did not think the new node falls within its AOI + (inconsistent judgement as to whether the new node is + *overlapped*) However, as the remote nodes only notify + once they go on thinking the new node has been learnt. + usually this can be avoided as the new node would likely + also be notified of this node. however, in this case + the node node was already over-connected, therefore respond + by shrinking AOI instead of initiating the connection. + + solution: add a buffer of 5 units when doing overlap check + when receiving NODE message. also decrease OVERLAP_RADIUS_MULTIPLIER + from 0.20 to 0.15 + + consistency 99.9036% -> 99.9950% + + parameters: + nodes: 25 world: (600, 600) steps: 990 + aoi: 100 connlimit: 10 velocity: 5 lossrate: 0 + + OVERLAP_RADIUS_MULTIPLIER consistency + 0.05 99.9147% + 0.10 99.9918% + 0.15 99.9950% + 0.20 99.9583% + + NOTE: dAOI consistency fixes in 06/13 and 06/14 are stored as + simu-case "undiscovery-5 (incremental fixes in dAOI)" + +UPDATE major modifications: + + replace MAX_SPEED, OVERLAP_RADIUS_MULTIPLIER, MAX_DROP_DISTANCE + in "vast_dc.h" by calculating max_velocity on the fly and + record remote node's most recent velocity. + + added BUFFER_MULTIPLIER and set to 4 initially. + + also change the return value of dist () from long to double + in "typedef.h" (Point class) + + + +2005/06/13 (1) +-------------- +- goal: debug dAOI inconsistency problem + + +DEBUG one dAOI inconsistency problem occurs because in adjust_aoi () + the judgement of whether a new adjusted AOI is smaller than the original + did not take into account of OVERLAP_RADIUS_MULTIPLIER.. + so adjustments weren't made when they should. + + 25 nodes @ 600x600 + consistency 99.8749% -> 99.9036% + +DEBUG another problem was caused by removing non-overlapped neighbors + that has a larger AOI (therefore considers myself as within AOI). + seems like the OVERCAP message was not processed properly. + however, also found that the node that disconnects still has + spare capacity. made a modification to allow continued connection. + + consistency 99.9036% -> 99.9335% + +DEBUG when doing remote_disconnect, a DISCONNECT message is generated + automatically at the disconnected node with timestamp 0, + which gives it higher priority than a potential OVERCAP message + (which notifies the disconnected node to shrink its own AOI) + + consistency 99.9335% -> 99.9616% + + +2005/06/12 (7) +-------------- +- goal: debug dAOI inconsistency problem + +DEBUG: found out the last joined node always knows neighbors from one step + later (so position is always inaccurate, which would increase + drift distance by a certain amount) + + fixed by making time_stamp to progress for every call to setpos() + even if the node does not actually move (unique ID not yet received) + consistency actually drops from 99.87% to 99.85% for 25 nodes in + a 600x600 environment. though avg_drift improves from 0.33 to 0.12 + +2005/06/09 (4) +-------------- +- goal: debug dAOI inconsistency problem + +DEBUG found one dAOI bug happens at the processing of HELLO message, + because it would send OVERCAP if a node is already exceeded in its + limit. however, if the HELLO message is sent from an already-connected + node, the same check still applies and could disconnect one that's + already-connected (violate valid scenarios for disconnection) + + solution: check if the sender of HELLO is already connected, if so + then simply just update the node information. + + +2005/06/08 (3) +-------------- +- goal: fix the process-within-same-timestep problem in the emulator (21:00) + debug dAOI inconsistency problem + +- added a _time variable to vast_dc class, also modify sendmsg(), recvmsg(), + and storemsg() in net_emu.cpp to include passing the current logical + time. Found out the difference after taking logical time into consideration + (so that no node may process message generated by other nodes within the + same time-step) is that consistency drops slightly (for 30 nodes) + but was subsequently fixed by adjusting OVERLAP_RADIUS_MULTIPLIER + from 1.10 to 1.20. Drift distance also increases (from 0.03 to 0.24) + +- 100% also achieved for 100 nodes with the new delay + + +2005/06/07 (2) +-------------- +- goal: debug basic model inconsistency problem (11:10pm) + debug dAOI inconsistency problem + +- add 'w' 's' 'a' 'd' 'o' commands for moving the GUI viewport + UP DOWN LEFT RIGHT and back to ORIGIN to the GUI interface + +- achieves 100% for 1000 nodes @ 1000 timesteps + +DEBUG improved consistency in basic model by resetting drop counters + when receiving NODE message for already-connected nodes + +BUG there's a nasty inconsistency problem that seems to result +(big) from Node A learns of Node B in the same step after Node A + disconnects Node B. So within the same step Node A does: + + 1. disconnects Node B (sends Node B a DISCONNECT message) + 2. learns of Node B through NODE again (sends HELLO, so reconnects to Node B) + 3. displays Node B correctly + + however, in the next step + 1. Node B process the disconnect message from Node A + and sends to Node A a DISCONNECT when doing a delete_node + (NOTE: usually Node B cannot send Node A a DISCONNECT as + the connection should already be broken, yet here + it is possible as Node A has re-established the connection again) + 2. Node B process the HELLO message (so learns of Node A again) + 3. Node A process the DISCONNECT from node B (so now Node B doesn't exist) + and sends another DISCONNECT to Node B + + at this point Node A doesn't see Node B, while Node B still sees Node A + in the next step + 1. Node B process the DISCONNECT (so finally Node B also loses Node A) + + and likely because the neighbors of Node A and Node B think + they've already done the notification, so no new notifications + are sent until several steps later. + +DEBUG fix the above bug by preventing sending DISCONNECTION again +(big) if delete_node is called as a response to a DISCONNECT message + (recorded as "undiscovery-4" in /simu-case) + + +2005/06/06 (1) +-------------- +- goal: debug dAOI inconsistency problem + +BUG: slight inconsistency might exist due to bootstrapping + in the beginning of simulation. + +IDEA: consistency under direct connection model without AOI + adjustment (the most basic model) is actually important, + in the sense that for application such as protein folding + (MD simulation), it is necessary to have 100% consistent + (fully-synchronized) simulation. This is when the real-time + requirement is droppable. + + +2005/06/04 (6) +-------------- +- put on first version of VAST's public website at sourceforge + (http://vast.sourceforge.net) + + +2005/06/02 (4) +-------------- +- goal: simulate dAOI model and tune + write VAST description for website at sourceforge + + +2005/06/01 (3) +-------------- +- goal: debug inconsistency problem (3:30pm) + 100% consistency @ 500 nodes (20:42) + +BUG: a)found out a rare inconsistent case where node A disconnects node B +(big) (as it's out of its AOI with enough drop count), however, just at + that particular step, node B actually has moved into node A's AOI. + But node A misses this as it hasn't known node B's most recent + position when making the disconnection decision. + b)Coupled with this problem is that the neighbors of node A and B + think they still know each other (could be because a new neighbor + notification was sent earlier, so these neighbors *thought* the + two know each other), so no neighbor notification is sent. + + possible solution: [1] use a "drop distance" instead of "drop count" + when the total distance has accumulated over a threshold then + do we disconnect. [2] A second solution is to do the disconnection + check (i.e. remove_nonoverlap()) after learning all the new positions + of neighbors. + +DEBUG: solve problem b) using solution [2] by calling + remove_nonoverlapped() before actually setting new position + in setpos() + +MILESTONE +DEBUG: achieved 100% consistency for up to 100 nodes (AOI-100) +(big) by adding "drop distance". After testing, use both + "drop count" and "drop distance" when deciding disconnection. + (inconsistncy still occurs by using "drop distance" alone) + +IDEA: after consistency is achieved, observe that OVERLAP_RADIUS_MULTIPLIER + indeed plays a role on consistency in that while both the multiplier + 1.10 and 1.05 achieves 100% consistency for 110 nodes, AOI: 150. + there are cases of inconsistency that recovers within 1 step for + multiplier 1.05. + +BUG: consistency drops considerably starting after 100 nodes, +(big) serious problem at 500 nodes, crash at 1000 nodes + initial overlay partition occurs during one 110 node trial + likely causes: + 1) joining too many nodes initially (should do so incrementally) + 2) buffer size inside Voronoi class too small + 3) initial ID-assignment problem (display incorrect ID after node 128) + +DEBUG solve scalability problem by treating case 3) + by replacing the original id assignment + _self.id = (id_t)*msg; + with + memcpy (&_self.id, (void *)msg, sizeof (id_t)); + + then, treat case 1) by run processmsg() inside CreateNode () + successfully run 500 nodes for 100 steps + +BUG: still has segmentation fault when running 1000 nodes + possible cause: exceed neighbor size maintained per node + (in SFVoronoi.h, set to 100 currently) + +2005/05/31 (2) +-------------- +- goal: debug inconsistency problem + +DEBUG found out one undiscovery problem is caused by the checking node + has not processed the MOVE commands of all neighbors, therefore + using an outdated position to the check. + + also, the checking node did not use OVERLA_RADIUS_MULTIPLIER when + doing the check. + +DEBUG: do neighbor discovery check after processing all the MOVE messages, +(big) this does improve consistency. (drift distance has decreased + dramatically) + + +2005/05/25 (3) +-------------- +- goal: debug inconsistency problem + +DEBUG found out the weird incorrect EN-judgement in some test case + was caused by when processing a NODE message, we may insert the new + node to Voronoi first then remove it. However, this practice + could redundently insert and remove from the Voronoi class when + the NODE messsage contains an already-known node. + after fixing this bug only minor inconsistency occur (fixed within + the next step). + +DEBUG with some tweaking consistency can now bring to above 99.97% for + up to 30 noes + +ideas: + two possibility for the low consistency: + - the new event model makes neighbor discovery slower (at least + one round is required to learn), this could explain many of + the inconsistency that fixes itself after one step + + - when processing message, if we immediately respond to MOVE + and respond with NODE, it might not give the most update/current + view, as subsequent MOVE command could change the Voronoi picture. + This can be fixed by first do all the movements, then respond + with NODE in a collective manner. this might explain the slight + drop of consistency in the basic model when # of nodes is high. + +2005/05/16 (1) +-------------- +- goal: debug inconsistency problem + + +2005/05/15 (7) +-------------- +- goal: debug inconsistency problem + +DEBUG woke up and realize the inconsistency problem could be due to + that i've combined the new AOI and new EN neighbor discovery + together in the current VAST (as opposed to using separate list + in VON). however, later found out the current inconsistency problem + is likely caused by incorrect judgement of EN (still to be investigate) + +2005/05/13 (5) +-------------- +- goal: debug inconsistency problem + +DEBUG solve a little bug where there are some "zombie" nodes that just + stand still. found out the problem was that delete_node() performs + checks whether the node to be deleted is an enclosing neighbor. + Actually it need not check (but this also tells us that, it's possible + that A disconnects B because A thinks B no longer overlaps and is + not an enclosing neighbor, yet A could still be an EN to B + (this could a problem.. isn't it?) + +2005/05/12 (4) +-------------- +- goal: 1. record/playback mechanism (done) + 2. low inconsistency debug + +- print results to log files (15:18) + + + +2005/05/11 (3) +-------------- +- goal: move to linux platform and run test simulations (17:40) + find out about the low consistency problem + +BUG: discover the problem from yesterday was that the math and C++ libraries + do not seem to link right. (even a small test program that calls + sqrt() cannot be compiled) + + solution: use 'g++' for compliation, and add "-lm" when trying to + link against the math library when using "gcc" + +BUG: incorrect results for 500 nodes, 1000 nodes would crash after step 2 + + +2005/05/10 (2) +-------------- +- goal: move to linux platform and run test simulations + find out about the low consistency problem + +BUG: while trying to port VAST to linux, met with this annoying bug: + +console.cpp: In function `int main(int, char**)': +console.cpp:52: use of `id_t' is ambiguous +/usr/include/sys/types.h:105: first declared as `typedef __id_t id_t' here +../../include/typedef.h:15: also declared as `typedef long unsigned int + VAST::id_t' here +console.cpp:52: `id_t' denotes an ambiguous type +console.cpp:52: confused by earlier errors, bailing out + + basically it says that 'id_t' has already being defined in "types.h" + therefore there's a double-definition problem. + + solution: use a work-around by avoiding the use of id_t in all + client programs. (for example, in statistics.h, I changed + map into map + +- got both /src and /src_sim to compile into linux shared library (.so format) + successfully, however, wasn't able to compile console.cpp properly + (appearantly due to linker problems where simple methods such as delete[] + and new could not be found.. a library path problem?) + + + + + +2005/05/09 (1) +-------------- +- goal: 1. implement connection beyond conn_limit restrictions (16:43) + 2. finish stat collection code (21:30) + 3. move to linux platform and run test simulations + +- both console and gui mode can run fairly smoothly now, and produce + good results. now the main problem visible is the dropped + consistency (reason unknown) + + + +2005/05/07 (6) +-------------- +- goal: 1. debug vast_dc using GUI + 2. move to Linux box, run simulation that collects results + +BUG: inconsistency of neighbor position occurs (esp. with large # of neighbors) + possible cause: + When A tries to connect to B, B refuses (out of its + own AOI). however, A is not notified so will keep "zombie" + node in its local view. + (however, problem persists when refusal mechanism is removed) + +DEBUG: the above bug was somehow solved by modifying the following: + 1) remove the overlap check in insert_node (so that insertion only + checks if it was already there) + 2) when responding to an EN with NODE, only relevant ENs + (i.e. those overlapped with the moving node) are sent back + (previously ALL missing neighbors are sent, therefore making + it necessary for insert_node to check if it was necessary + to connect upon receiving NODE, now we just accept whatever's + notified by NODE unconditionally) + + some "slight" inconsistency is still observed when a new node is + discovered, however, it's usually quickly fixed when the moving node's + next position update is received (reason unknown). + +IDEA: define a new 'topology consistency' matrics: + should take into account difference between observed & unobserved + (mix 'consistency' with 'drift distance') consisteny is weighted + against difference in coordinates + +IDEA: different types of disconnection needs to be categorized: + + Scenario Response Message + ------------------------------------------------------------------- + 1. leaving overlay reaction not needed DISCONNECT + 2. AOI no longer overlap reaction not needed (?) DISCONNECT + 3. refuse connection adjust AOI OVERCAP + 4. shrink AOI adjust AOI OVERCAP + +TODO: joiner exceeds connection limit should be taken care + +IDEA: title for a new paper: +(big) "Spatial and Temporal Locality-based Visibility" or + "Scalalbe Visibility based on Spatial and Temporal Locality" + + idea is simple: how do you see "more" in AOI, for a given amount of + bandwidth, yet require DIRECT connection among all nodes? + + Answer: you set up different update frequency with each sender. + sort of like using a big pipe for more nearer sender, and smaller pipe + for further sender. But the combined size of pipes are the same, + and if you extend more and more (receiving from more and more users), + the pipes will become smaller and smaller. If done well, this could + bring very nice "granuality" into update frequency and potentially + see large number of users, and can even individually priortize + update frequency. + + +BUG: after implementing pieces of the 'statistics' class. + program just halts/crashes for no appearant reason, even simple + console mode wouldn't work. + +DEBUG: found out it was a small "fix" in net_emu.cpp that caused the problem + (and I even removed the whole new 'statistics' class!) + + // failure likely due to memory allocation problem + if (newnode->size == 0) + return 0; + + it skipped the proper insertion to message queue, ignoring the fact + that size of 0 can in fact be legal. + + +2005/05/06 (5) +-------------- +- goal: finished debugging the networked layer + + +BUG: very weird bug in send_nodes () + had tried to use various methods in the map _id2node + for example: + + map::iterator> _id2node; + map _id2node; + + but when trying to access a particular node in send_nodes () + the node access, whether using iterator or Node * would + give very strange/incorrect data for a particular node_id + +DEBUG: possible cause for the above: + after insertion to a std::vector, the ordering will change + (i.e. cannot assume the first inserted can be accessed with index 0) + + solution: change the types of _neighbors and _id2node to + + std::vector _neighbors; + map _id2node; + +DEBUG: found out a major bug left from previous implementation of VON +(big) it is possible that a remote node has just been disconnected, + yet it has sent MOVE messages to me. therefore when trying + to process this MOVE message, the remote list's EN list would + not be found. It was worked around by 'new' an EN list, however + the real solution should be simply to ignore the MOVE message + +- finished networking layer debug, also created preliminary GUI interface + (global & switchable local view, AOI-radius display, Voronoi edges) + + +2005/05/05 (4) +-------------- +- goal: finished debugging the networked layer + +DEBUG: the crash when calling vast_dc destructor bug was caused by + incorrect use of iterator + + in insert_node() the code was + + _neighbors.push_back (node); + _id2node[node.id] = _neighbors.end (); + + // types + std::vector _neighbors; + map::iterator> _id2node; + + the proper way should be: + + _id2node[node.id] = _neighbors.insert (_neighbors.end (), node); + + +2005/05/03 (2) +-------------- +- goal: finished implementing the networked layer +- implemented but buggy (crash when destructor of vast_dc is called) + + + +2005/05/02 (1) +-------------- +- goal: implement the networked layer + +some ideas: A universal (for both emulation and actual network) network + interface for the protocol layer to use (class 'network') + + In emulation, there'll be a shared node discoverer object + (class 'netemu') used to find pointer to different 'network' + objects to send messages to. each network object maintains + separate "receive queue" + + when network object is first used, it would obtain a temp. + id from the discoverer object, until it gets its unique ID + from gateway and register with the discoverer object + +- had done a preliminary compilable version of a switchable network layer + + +2005/04/29 (5) +-------------- +- goal: implement the networked layer + + +2005/04/28 (4) +-------------- +- goal: implement the networked layer + + +2005/04/19 (2) +-------------- +- goal: implement a simulated network and have some actual simulation runs + + +2005/04/18 (1) +-------------- +- goal: implement VAST DC model as much as possible + +- finished a prelimiary implementation of vast_dc.cpp + now onto network.cpp + +2005/04/16 (6) +-------------- +- goal: implement VAST DC model as much as possible + + +2005/04/15 (5) +-------------- +- goal: implement VAST DC model as much as possible + +- implemented processmsg() all messages except MOVEs + +2005/04/14 (4) +-------------- +- goal: implementation of the direct connection model and + simulated network layers? (miracle required.. :) + +- create a factory class for vast called 'metaverse', which offers + create() and destory() + +2005/04/13 (3) +-------------- +- goal: implementation of the direct connection model and + simulated network layers. + +- separated vast.dll and VASTsim.dll developments, an outside app that + uses VASTsim.dll will only need to include VASTsim.h, provided + that the directory of /src and /src_sim are located in the same place. + (better way to do it?) + + +2005/04/12 (2) +-------------- +- goal: create workable GUI suitable for displaying + - global view of nodes (check) + - local view of a particular node + - regular refresh/updates (check) + - view-pan/move + - enlarge/smaller (optional?) + +- studied DX9 in the morning, but decided to use GDI instead. + got a working version. also created the VASTsim.dll library apart from + vast.dll + +2005/04/11 (1) +-------------- +- goal: define interface and code structure + +- defined vast.h main interface used by outside app (virtual class) + vast_dc.h direct connection model implementation + typedef.h main data structures + network.h network layer + config.h platform-specific configurations + Voronoi.h Voronoi services + SFVoronoi.h Steve Fortune Voronoi + + vast_sim.cpp a simulator + behavior.h behavior model (random movements) + +2005/04/10 (7) +-------------- +- start log for VAST + +VAST: VON-based Adaptive Scalable Toolkit + + - a light-weight C++ library to support VON + - three layers: 1) simulator 2) protocol 3) network, each is substitutable + - well-defined interface, easy to use + + - clean, fast, solid + - ACE is confined to network layer + +- goal today: define interface and code structure + + + diff --git a/backup b/backup new file mode 100644 index 00000000..d198eced --- /dev/null +++ b/backup @@ -0,0 +1,3 @@ +git add . +git commit -a -m $1 +git push From 06589fb2dfbebbdeacc1cd7131ebd32016f4a2a6 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Fri, 28 Sep 2012 19:09:37 +0800 Subject: [PATCH 002/141] VSS server JSON response fixed --- NN.bat | 2 +- WORKLOG.txt => VAST WorkLog.txt | 257 ++++++++++++ VAST.js | Bin 8912 -> 16990 bytes VON_peer.js | 45 +- VSS/VSS.js | 17 + VSS/handler.js | 396 ++++++++++++++++++ VSS/router.js | 37 ++ VSS/server.js | 32 ++ net_nodejs.js | 52 ++- {socket.io => vast.io/socket.io}/socket.io.js | 0 .../socket.io}/socket.io.min.js | 0 .../vast.io-client (local).html | 0 .../vast.io-client.html | 0 .../vast.io-server.js | 5 +- vast.io.bat => vast.io/vast.io.bat | 0 vast.io.js => vast.io/vast.io.js | 0 vast_net.js | 70 +++- vast_types.js | 4 +- 18 files changed, 840 insertions(+), 77 deletions(-) rename WORKLOG.txt => VAST WorkLog.txt (96%) create mode 100644 VSS/VSS.js create mode 100644 VSS/handler.js create mode 100644 VSS/router.js create mode 100644 VSS/server.js rename {socket.io => vast.io/socket.io}/socket.io.js (100%) rename {socket.io => vast.io/socket.io}/socket.io.min.js (100%) rename vast.io-client (local).html => vast.io/vast.io-client (local).html (100%) rename vast.io-client (dev).html => vast.io/vast.io-client.html (100%) rename vast.io-server.js => vast.io/vast.io-server.js (97%) rename vast.io.bat => vast.io/vast.io.bat (100%) rename vast.io.js => vast.io/vast.io.js (100%) diff --git a/NN.bat b/NN.bat index ef1447ba..20edbfea 100644 --- a/NN.bat +++ b/NN.bat @@ -1 +1 @@ -node test_von_peer 3770 127.0.0.1 +node test_von_peer 37700 127.0.0.1 diff --git a/WORKLOG.txt b/VAST WorkLog.txt similarity index 96% rename from WORKLOG.txt rename to VAST WorkLog.txt index 44334afa..b16d1fdf 100644 --- a/WORKLOG.txt +++ b/VAST WorkLog.txt @@ -20,6 +20,7 @@ BUG (*) denotes design bug: 2012-07-23: voro.overlaps returns 'false' always when there's only one region for any point 2012-08-29: scaling many nodes will cause TCP connection errors 2012-09-03: EADDRINUSE crash bug (after running a while with >50 nodes) +2012-09-24: send_msg in vast_net does not necessarily send out in FIFO order @@ -48,6 +49,259 @@ BUG (*) denotes design bug: - join retry timeout is buggy - 2010-10-06 after repeated join/leave of worlds, cannot join again (gateway could not assign origin matcher) +Overall Goals: +extract non-VON-specific code from VON_peer as common code for handlers +build VSS server (to take & respond RESTful pub/sub commands) +connect VSS functions with VON peer +debug VON peer usage from VSS server +allow ident be queried when get subscribers + + +2012-09-28 (5) +-------------- +goal: debug & test JSON response + debug node register / revoke + + + + +2012-09-27 (4) +-------------- +goal: debug & test JSON response + debug node register / revoke + +- review current code & procedure. + + +2012-09-26 (3) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + connect VSS functions with VON peer + debug VON peer usage from VSS server + allow ident be queried when get subscribers + add revokeNode to VSS function + +- revokeNode & registerNode added + +2012-09-25 (2) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + connect VSS functions with VON peer + debug VON peer usage from VSS server + debug send_msg mis-ordering + +for the 2012-09-24 bug: +- add queuing up messages to be sent, if connection is not yet established. + can run till 50 nodes ok (on PC), but crash due to EADDINUSE for 100 nodes + but we still get remote id is NaN error + +- might consider add explicit id exchange during handshake, to ensure remote id is always available. + +- seems the NaN id is reduced (for 50 nodes), but some nodes still get NaN id + at times (esp. as the # of nodes increases). + + + +2012-09-24 (1) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + connect VSS functions with VON peer + debug VON peer usage from VSS server + +- test again test_VON_scale for scaling VON server @ dev server. can run up to 50+100 = 150 nodes. + however, client movement in browser stops when all nodes are joined + + when all existing nodes at server are closed: + gateway (test_VON_peer) experience this: connect ECONNREFUSED and it'll crash + +- test run 100 VON nodes @ dev server, seems to continously moving notice the following: + * CPU is used at 100%, RAM 10 - 15% (out of 512MB, left 168 MB) + * CPU is used by the test_VON_scale + * movement becomes slow after a while (data accumulated?) + * file handles were in the range of 10,000.. while after termination, only 700 file handles are used normally + +BUG: found a bug caused by the asyncrous nature of js function calls + when a newly joined VON peer tries to send HELLO / EN to a newly learned neighbor, + the EN message can be sent *BEFORE* the HELLO is sent (or before the connection is established) + the reason is that when sending the first HELLO message, connection is yet established, + so an attempt is made to connect. + but this is returned directly, so the next message is sent and stored. + when the connection is finally made, the 2nd message gets delivered first before the first message. + + basically we cannot assume that two messages will be sent in a FIFO order. + + +2012-09-23 (7) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + connect VSS functions with VON peer + test VON peer usage from VSS server + +- done integrate + publishPos + subscribeNearby + unsubscribeNearby + querySubscribers + + +2012-09-22 (6) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + connect VSS functions with VON peer + +- done parameter extraction from RESTful request +- publishPos, subscribeNearby done preliminary integration with VON peer + +2012-09-21 (5) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + connect VSS functions with VON peer + +- implement basic functions wtih VSS server, will now need to use & connect them with VON peer + + +2012-09-20 (4) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + parameter breakout & analysis + +- done parameter breakout + + +2012-09-19 (3) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + build VSS server (to take & respond RESTful pub/sub commands) + +- done creating basic layout for VSS server + +2012-09-18 (2) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + +- add VSS server binding to provide pub/sub functions +- study about js inhertance & prototype mechanism + +2012-09-17 (1) +-------------- +goal: VAST porting + basic pub/sub functions + implement VASTClient + implement join() + build sharable network layer (for VASTClient & VON) + extract non-VON-specific code from VON_peer as common code for handlers + +- still stuck at not sure how to extract net layer so the following effects are achieved: + * multiple logics supportable on same net layer + * logic are independent of each other (can be modularly inserted/removed) + * do not use a singleton (so that multiple net instance can exist in same process) + + +2012-09-16 (7) +-------------- +goal: VAST porting + basic pub/sub functions + implement VASTClient + implement join() + build sharable network layer (for VASTClient & VON) + +- evaluate and decide to follow original VAST C++ approach (message handler) + for message processing, pending fixes for js-specific behaviors. + + +2012-09-15 (6) +-------------- +goal: VAST porting + basic pub/sub functions + implement VASTClient + implement join() + build sharable network layer (for VASTClient & VON) + +- create message_handler.js + can plugin independent logic to handler, while sharing a common network layer for send/recv + + required methods: + + processPack() handle incoming messages relevant to this handler + sendMessage() send a message to one or more targets at a different handler + tick() perform incoming message processing + + +2012-09-14 (5) +-------------- +goal: VAST porting + basic pub/sub functions + implement VASTClient + implement join() + build sharable network layer (for VASTClient & VON) + +- need to consider: + whether functions of VON/VSO/SPS (& possibly future VoroCast) can be completely de-couple? + also the net layer can be physical or simulated + in other words, any one of them can be enabled/disabled. + +a possible division of labor: + +VON: peer connectivity, neighbor discovery within peer's AOI + - find gateway and join network + - position movement + - update from neighbors + +SPS: pub/sub record keeping, pub/sub matching, request passing + - pub/sub request + - sub record storage + - pub request passing to neighbors + - pub/sub matching + - delivery of messages + +VSO: region resizing, pub/sub record migration, fault-tolerance (?) + - load detection + - overload handling (resize) + - overload handling (join) + - underload handling (resize) + - underload handling (leave) + - load migration (ensure consistency & fault-tolerance) + +VCAST: request forwarding + + +Dependencies: +VSO -> VON +VCAST -> VON/VSO +SPS -> VON/VSO/VCAST + + +2012-09-13 (4) +-------------- +goal: VAST porting + basic pub/sub functions + implement VASTClient + implement join() + build sharable network layer (for VASTClient & VON) + +- reorganize join procedure. +- decide to use singleton mode for net layer sharing + +2012-09-12 (3) +-------------- +goal: VAST porting + basic pub/sub functions + implement VASTClient + implement join() + + +- see that the network layer needs to be shared by VON, VAST, VSO... +- might need to pass in net layer to VON (and also VASTClient), + and create a separate MessageHandler class (any other way?) + + 2012-09-11 (2) -------------- @@ -55,6 +309,9 @@ goal: VAST porting basic pub/sub functions layout modular framework (for independent features) + +- upload vast.js codebase to github + - basic idea for making VASTClient, VASTRelay, VASTMatcher modular: VASTMatcher: diff --git a/VAST.js b/VAST.js index 21d5fcd9488f454142acd25c3975e22af3283635..3c24b0f8ff1501e796547485821f3b49cffeae3b 100644 GIT binary patch literal 16990 zcmcheX>(jh6^8FusNz2irV392884}jB0xZvV?`-%SV~g)G^N!RmL(y{c7&6^4!lpl zbJX4U_DCbyP*bD3O!wKhKHd7yzkk*Jvb)uN+;JW0I_kdbmb$fWz5BiHH}pN}wshUs zcT<0N^yWx^ce>;5lkT)T)Zcg>bq9KX+P&4=s%^h{)Zn^tN*SpV5jUMAJ1FS6%E1DzJ7fwsOWT0^k(c2(=#KPhYbM#=zQ!+ z!oIC{n~A=l=7Hc^GHZ#NESXXFKyS>!hN#}t(?OEgj_y6bKiN@jEwyUr-TX&{qaVy6ou=Z@- z5uxxo?01yB>{NWCjT2E2c;Y&eOzq`(0(bjKVrUa9ve$!SFFhko;^>yf645NlY!M&U zSNDbGKoK+R+?ZN14a*?P5VsGdWqjzZq=2p1LJ##Oq(0xjvt(L+4%Ag)*pe){|19}3 z(6FtS$>y-cwHXBu5=~&mUf>7KF!P@7PBaP*d481aXrvr)H)Vm_ivD-HPg737ZoU=F zrGfr#r8m%=^B$ue`{8?2lo1KGbdUEsM(pY>TFqIFD7vrbSNi22iOQTk8AZg~(rm}O zY)i&=qTi47fjh=nYbSc+80i>Y$6c}xF`Jz1d;&dV@%dC=VjFx}A1y5Q?IC4fRCCJt zw zv+n0=)Q0?STexBr-UuDoM#!~41IqBmjQ=E!#~O*?MBRE0`4xYPIaC0L()m=Qx6@o~ zY1jH!ai)f>WwR*@ODH|l~Z3dH~TXU`q$O&ezI%x)c$KahIUD$=u58cLGTv1OA4u$Tz_r~*0D5bTG1hv zhQ}@Pxf+FitavyPn)(lnfxV>E_VMPNLR;;k^kp8dMrl-1we8k+SW+3#iQr|#K(^($ zScBLqwv;l$x_x1uU)DnD?10jt+EWO{qhMUo9T zId=v>!xhqVVWesx0}_{>>o56+`ioit3A-LcMy@nUnpu&#?iep@jal3IF2lK;>Jq=b zw9H52S(L~T1lx2J^l0m{mf#RhnP)8>!$BrQMN2D7FUPHtzB+$iMBBw4Q^PU#+Uk)T z@dl0Y_d3*4g<=h?T8XMeKddjH)iTT4>)!<&(^<>5jL<=`?eeWKIrCBhx@sw9>5Q~3 z>LT-9EVDjU@f>hWU0aIOL1aAY(^!*uMRc5`Q+s_xMAm-U+WK*{Wp_c7uVmh^t$-Qz zdM!oPXsj1=olWF`qf+}>digHrO;#CTBa2d7lHaKS@N^)Tb4=M&RmErejoOs@@Udo; z`RPQmx!6W7;_#>k-BwY zd7N^?OFcoi*ATU5lpHu+m| zw_#&w$a1!>kRSOMJBteQtYPJA8n-WPy*~wL2dxu#h$ToK$k?`Pz+qfiJI@x^#UK2? zO8#8?c)w;YGYA<9J^odmF{JBzQS7RPao3@(rYl!r^c3zlFg~1lEi3OcVYHRUs%#PO z4UE(iRAPoydSLh@6$qN~pYdr5d(Jv?%?y9Lc)l8%Y%_!7{HS zQ|#S7==HmMK&#!&6q%xW1$rOzvt)}@rq{)L5oY9>F|_R+{+}jmCqHjpn~oO$BgLiY z;pKX7OH{>5z%d(pUlkQt6Y7XWi~T zPk;X)=<`2VgKIW-jp}YT^pY*ubAdi;4rUDdTJCJ*F4ja6sr@==C@K77>bu>(fId1% zJG0CKZugpZlAk~ao|fy)k5v&NKfd_EKc#Q>kEn}ps*89laq~*E;c24Y+lk(PH0vQT zt6XPNKR!>e-UtugNza-7wL9li`O_1>|29h`u&uHE4@nNBkR~81~Y#Ev<{n`}%&7#!k0Ip5Y6L1B9bTQkXLUmCU}uE7gbuyul+=VT z;ZH+a*vl&^DDO>;_dRzLF0U{m^OBd*m3=LI1>Dg+=Dy$I$+4{LB}FAiZ-^Y&?kk06 zO8!hGo{q&JC-w#0wPY8O`1?9hTFwd!n92%WRk*)%zc(LixS>6ayNdb~#eRS9NT;9b z&F6Y^Tff)6@(epyM=iK_iuf3EH#dE0L??rKlNjZF=%KGu7uS^z`XNr2o$9t6JSzHn z_JjPcPmwt;A(?XQ&$Tig_1N8v9jjW_Wp=K+jah>I8R#Q+-t8w;MYa5gRJCj9tet-4 zn(^l`?lM{~SErz?Bx`>edThpC&Vut{yqah~Ek~#MazG~i-L=~0j5Y6sU^(vZU{&7R zh_30fe2N~BcSFdqWKVLx-<28&EJ1rb&$~_)Q@p+!=6B}QcE)LoT&vjDu^HCkmaNzL zxj#}@u`Noe$;d<0gMU7kIcIUc6#o2~CVuN~Oi8mT?RTyEE4d|Qs8vBlb~UW`qpjaN zI;rZVHf{YmAD*hZJ5p0g-TE$PuEou)jIwb;6w&a5i>BKG~PTT3sh7q7Tu z4T{w{QoMY;o zT*913g=dr1$=uGE^?qGY6Jwkeh`4QAc3k>VvU1%^zt88_wkjTsnYhDP+gTlC|v zM~eIrEvQ9<(kvTzA|FhXee-@QD>+hxaXp!>#q{F^j3TS%Q5kaPXnq%~pCjhkREsN9 z0R`l>IMYx@E$8vbn!`Hsa>>=1^N}=Do}bA1llaORj)?RjE7OQZp~b!Q#VKrY9Z}?- zq`s>Uc3kt}Cr2vpYjd7&cjhUk2#uJkI73E7R6=||Du&QqJ##ot=S<$4}@gP#*h)>!PmXE~qF*77x;mJMXf z$1~e8D}280PUGWv|C+AMT~~jQSm-*?o)3lYNat9ECgMGHGPQJ2-da!h^%dQ5=D`vD zx8SqpuzZ5g{&RUwcuO$AL?u8zs3p;#J%ki1d}5Z*JBN1-t>}GEQCMe>RU5CXmqOPi zW2fUxRnN*ENyjO8WbXZ5cYe_sJqmYQy$_tB5MS&WpA@he#q5apb*~k^!A^(MHH+7S z!yd9*2)2CW!l%7vjh@t3vwF=^2zdSLLs`L!WbPBtoCdmi z&NaIpSjRKFXV z5ft9-#qh~^R@^Pf(G7_mAM=GZb2=s>&_ojS*)S|3C=K2C8Y3o(;dBVlFjiaZW!~)m zIMWd@+ZY3Xi!V#8%zAK2MHCfeUxNKzuf?Ex|0C}+`8*LPRw8b;`kObBuGmSVADjPA zh+Vct>F_$_RMq*Hvp#I*4LD0Pjk!u3FQ^CP*49P9BMM?4ROICK1AXzqV?~NDlC2RP z_eIl=_By-+;WhkNCwu$WfG-Z;THh@VM6{xEqDFjP#jB{U!*20JM@&~Z#Il?E{!ZUM zmlJVxm}(JLbnKyE>+|fjMVoET-WK(>*R8qFi6r3J`>2eq^P_7r@0LX#CTbo@vYg0> zEEE6XLZ&W#yga!Ht`?nB`8IR%=}vpw@NLAcMSf{Vv+&XOwMCt!Nc1RI&AHc&*H~_x zz&}gU#2(``8L@}VO{S%ai5Sm*F%_D-fQG%HRW`9S>XBSWg1_7)0wepf?hLi)HpT0L zPZel-$LE<8ozLHg<^K@xq}`6&=N8w((XRn6&i~$%YRAqN^ou*#&ZfwLst^e~$8dHY zY34I=@QS}#;<@^1;mp-dhD<~Q_BWCuo=9DVFJh(CJh_5sS4?4RKwP};h+X)Y9oH7s z*+zX@Ijl1(Ia}z(_7a(wZ0U0;@HWi6@%y;x%RyyCylL9$=fBUE6#Mv(vZw~pN{g3( zP)3D`AEw3$n$oi2qS@t@-jwBNW zck{3?+oLCf;O>mqEG55j#(jR?sck8uL!Dh}D#xh~irv;nlEsR0&Wd)fIfwC7_odF3 zBq{#8ke%&5FLgy+%E)$Dt~D-OJC?KadaHzB53B!|3rXXD@AR#$$h4JE!#B$Rl5w{q z*68(_Vd@>?2VIGBUF#@AT~R{MG`G5s^Jveo@B%W#yc7EGDr}cYKA)Q6eUm{OeW4*H(t zxREvDIKl2noIp1fL}zx<=;u=b*OjhoGU_$eTxx9 diff --git a/VON_peer.js b/VON_peer.js index 060b22ef..afb229af 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -114,9 +114,7 @@ var NodeState = { ABSENT: 0, QUERYING: 1, // finding / determing certain thing JOINING: 2, // different stages of join - JOINING_2: 3, - JOINING_3: 4, - JOINED: 5, + JOINED: 3, }; // status on known nodes in the neighbor list, can be either just inserted / deleted / updated @@ -142,12 +140,6 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // public methods // - // callback to use once join is successful - var _join_done_CB = undefined; - - // interval id for removing periodic ticking - var _interval_id = undefined; - // join a VON network with a given gateway (entry) var _join = this.join = function (GW_addr, aoi, done_CB) { @@ -200,7 +192,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // send out join request // TODO: if id is not correct, remote host will send back correct one _net.storeMapping(VAST_ID_GATEWAY, GW_addr); - _sendMessage(VAST_ID_GATEWAY, VON_Message.VON_QUERY, _self, VON_Priority.HIGHEST); + _sendMessage(VAST_ID_GATEWAY, VON_Message.VON_QUERY, _self, VON_Priority.HIGHEST); }); }); @@ -344,11 +336,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { } // check if a given ID is an existing neighbor - var _isNeighbor = this.isNeighbor = function (id) { - // list all neighbors - //for (var key in _neighbors) - // LOG.debug('isNeighbor: ' + _neighbors[key].id); - + var _isNeighbor = this.isNeighbor = function (id) { return _neighbors.hasOwnProperty(id); } @@ -381,7 +369,6 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // var _isSelf = function (id) { - //LOG.debug(typeof _self.id + ' self.id: ' + _self.id + ' ' + typeof id + ' id: ' + id); return (_self.id == id); } @@ -389,7 +376,6 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // NOTE: right now we only consider circular AOI, not rectangular AOI.. // NOTE: l_aoi_buffer is a paramter passed to VON_peer when creating instance var _isAOINeighbor = function (id, neighbor) { - //LOG.debug('_isAOINeighbor called'); return _voro.overlaps(id, neighbor.aoi.center, neighbor.aoi.radius + l_aoi_buffer, OVERLAP_CHECK_ACCURATE); } @@ -414,7 +400,6 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var timeout = period * _net.getTimestampPerSecond (); return ((_tick_count - _neighbors[id].endpt.lastAccessed) < timeout); */ - //return true; } // @@ -452,7 +437,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // update node status _updateStatus[node.id] = NeighborUpdateStatus.INSERTED; - LOG.debug('insertNode neighbor (after insert) size: ' + Object.keys(_neighbors).length + ' voro: ' + _voro.size()); + LOG.debug('[' + _self.id + '] insertNode neighbor (after insert) size: ' + Object.keys(_neighbors).length + ' voro: ' + _voro.size()); return true; } @@ -516,11 +501,8 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var center = new VAST.pos(); center.parse(node.aoi.center); node.aoi.center = center; - //delete node.aoi.center['id']; - //delete node.aoi.center['voronoiId']; return node; } - // // regular processing (done periodically) @@ -569,7 +551,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var new_list = []; // list of new, unknown nodes var target; - LOG.debug('voro nodes before insertion: ' + _voro.size()); + //LOG.debug('voro nodes before insertion: ' + _voro.size()); // loop through each notified neighbor and see if it's unknown for (var target in _new_neighbors) { @@ -591,7 +573,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { } } - LOG.debug('voro nodes after insertion: ' + _voro.size()); + //LOG.debug('voro nodes after insertion: ' + _voro.size()); // check through each newly inserted Voronoi for relevance for (var i=0; i < new_list.length; ++i) { @@ -688,13 +670,6 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // TODO: determine whether clear or new is better / faster (?) notify_list = []; - /* - map *known_list = new map; // current neighbor states - map::iterator it; // iterator for neighbor states - - id_t id; - int state, known_state; - */ var known_list = {}; // current neighbor's states var state, known_state; @@ -1123,8 +1098,6 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { if (_potential_neighbors.hasOwnProperty(from_id) === true) { // insert the new node as a confirmed neighbor with updated position - //var pos = new VAST.pos(); - //pos.parse(pack.msg); var neighbor = _potential_neighbors[from_id]; neighbor.aoi.center.parse(pack.msg); @@ -1326,7 +1299,13 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // // internal states // + + // callback to use once join is successful + var _join_done_CB = undefined; + // interval id for removing periodic ticking + var _interval_id = undefined; + // node state definition var _state; diff --git a/VSS/VSS.js b/VSS/VSS.js new file mode 100644 index 00000000..b908130b --- /dev/null +++ b/VSS/VSS.js @@ -0,0 +1,17 @@ + +// include VAST +require('../common'); +LOG.setLevel(3); + +var server = require("./server"); +var router = require("./router"); +var handlers = require("./handler"); + +var handle = {} +handle["publish"] = handlers.publish; +handle["subscribe"] = handlers.subscribe; +handle["unsubscribe"] = handlers.unsubscribe; +handle["query"] = handlers.query; +handle["revoke"] = handlers.revoke; + +server.start(router.route, handle); \ No newline at end of file diff --git a/VSS/handler.js b/VSS/handler.js new file mode 100644 index 00000000..ac4d0ab3 --- /dev/null +++ b/VSS/handler.js @@ -0,0 +1,396 @@ + +// +// local private variables to keep track of created nodes +// + +// include VAST +require('../common'); + +// default port for connecting / creating VON nodes +var _VON_port = 37700; + +// default radius when first joined +// TODO: need to have default radius? +var _default_radius = 0; + +// number of nodes already created +var _nodes_created = 0; + +// API keys -> layers -> nodes mapping +// NOTE: it's a 3-dimensional array +var _keys = {}; + +// +// actual execution code +// +var _publishPos = function (node, pos, radius) { + + // build new AOI info + var aoi = new VAST.area(pos, radius); + + // perform movement + node.move(aoi); +} + +// +// helper code +// + +var _registerNode = function (pos, info, done_CB) { + + // extract AOI for the VON node to create + var aoi = new VAST.area(pos, _default_radius); + + // specify where to locate VON gateway + var ip_port = new VAST.addr('127.0.0.1', _VON_port); + var new_node = new VON.peer(VAST_ID_UNASSIGNED, ip_port.port + _nodes_created); + _nodes_created++; + + LOG.debug('new_node (before join): ' + new_node.getSelf().toString()); + + // join in the network + new_node.join(ip_port, aoi, + + // done callback + function (self_id) { + + LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + new_node.getSelf().id); + + // keep track of newly joined node in internal record + if (_keys.hasOwnProperty(info.apikey) === false) + _keys[info.apikey] = {}; + if (_keys[info.apikey].hasOwnProperty(info.layer) === false) + _keys[info.apikey][info.layer] = {}; + + _keys[info.apikey][info.layer][info.ident] = new_node; + + // check content + LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); + + done_CB(new_node); + } + ); +} + +var _revokeNode = function (info, done_CB) { + + // check if node exists, return error if not yet exist (need to publishPos first) + var node = _getNode(info); + + if (node === undefined || node === null) + return done_CB(false); + + //destroy node + node.leave(); + node = null; + delete _keys[info.apikey][info.layer][info.ident]; + + // check if we need to remove layer and/or API key + if (Object.keys(_keys[info.apikey][info.layer]).length === 0) + delete _keys[info.apikey][info.layer]; + if (Object.keys(_keys[info.apikey]).length === 0) + delete _keys[info.apikey]; + + done_CB(true); +} + +// send back response to client +var _reply = function (res, res_obj) { + + // return response if exist, otherwise response might be returned + // AFTER some callback is done handling (i.e., response will be returned within the handler) + if (typeof res_obj === 'string') { + LOG.debug('replying a string: ' + res_obj); + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end(res_obj); + } + else { + LOG.debug('replying a JSON: ' + JSON.stringify(res_obj)); + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(res_obj)); + } +} + +// get a specific node given its API key, layer, and node ident +// returns 'undefined' if key info is missing +// returns 'null' if not found +var _getNode = function (req, res) { + + // check if any essential info is missing + if (req.apikey === '' || req.layer === '' || req.ident === ''){ + if (res !== undefined) + _reply(res, ["", ["key info (apikey/layer/ident) missing"]]); + return undefined; + } + + for (var key in _keys) + LOG.debug('key: ' + key); + + if (_keys.hasOwnProperty(req.apikey)) { + var layers = _keys[req.apikey]; + + for (var layer in layers) + LOG.debug('layer: ' + layer); + + if (layers.hasOwnProperty(req.layer)) { + var nodes = layers[req.layer]; + + for (var node in nodes) + LOG.debug('node: ' + node); + + if (nodes.hasOwnProperty(req.ident)) { + return nodes[req.ident]; + } + } + } + + // no node found, warn in advance + if (res !== undefined) + _reply(res, ["", ['node not yet created']]); + + return null; +} + +function publish(words, res) { + LOG.debug("Request handler 'publish' was called."); + + var request = {}; + + switch (words[2]) { + + // publishPos + case 'pos': { + LOG.debug('pos ...'); + // need to update position, or create new node if nothing exists yet + + // extract parameters + request = { apikey: words[3] || '', + layer: words[4] || '', + ident: words[5] || '', + x: Number(words[6]) || 0, + y: Number(words[7]) || 0, + z: Number(words[8]) || 0 + }; + + // verify parameter validity + LOG.debug('request: ' + JSON.stringify(request)); + + // check if node's already created, if so then send update + // if not then need to create a new VON peer node + var node = _getNode(request); + + switch (node) { + // parameter invalid + case undefined: { + _reply(res, ["", ["key info (apikey/layer/ident) missing"]]); + break; + } + + // no node exist, create new + case null: { + // if node does not exist, create one + var pos = new VAST.pos(request.x, request.y); + + // append additional z value + pos.z = request.z; + + _registerNode(pos, request, function (new_node) { + + LOG.debug('new_node: ' + JSON.stringify(new_node)); + + // send back node creation response + _reply(res, ["OK", []]); + }); + break; + } + + // publish pos + default: { + _publishPos(node, request, node.getSelf().aoi.radius); + _reply(res, ["OK", []]); + break; + } + } + break; + } + /* + case 'area': { + LOG.debug('area ...'); + break; + } + */ + default: { + _reply(res, JSON.stringify(request)); + break; + } + } +} + +function subscribe(words, res) { + LOG.debug("Request handler 'subscribe' was called."); + + var request = {}; + + switch (words[2]) { + + // near + case 'nearby': { + LOG.debug('nearby ...'); + + // extract parameters + request = { apikey: words[3] || '', + layer: words[4] || '', + ident: words[5] || '', + radius: Number(words[6]) || 0 + }; + + // check parameters + if (request.radius <= 0) { + // return an error + _reply(res, ["", ['radius is 0 or less than 0']]); + break; + } + + // check if node exists, return error if not yet exist (need to publishPos first) + var node = _getNode(request, res); + + if (typeof node === 'object') { + // update AOI radius for area subscription + _publishPos(node, node.getSelf().aoi.center, request.radius); + + // return success + _reply(res, ["OK", []]); + } + break; + } + default: { + _reply(res, JSON.stringify(request)); + break; + } + } +} + +function unsubscribe(words, res) { + LOG.debug("Request handler 'unsubscribe' was called."); + + var request = {}; + + switch (words[2]) { + + // near + case 'nearby': { + LOG.debug('nearby ...'); + // ensure this method doesn't get abused + + // extract parameters + request = { apikey: words[3] || '', + layer: words[4] || '', + ident: words[5] || '' + }; + + // check if node exists, return error if not yet exist (need to publishPos first) + var node = _getNode(request, res); + + if (node === null || node === undefined) + break; + + // update AOI radius for area subscription + _publishPos(node, node.getSelf().aoi.center, 0); + + // return success + _reply(res, ["OK", []]); + break; + } + default: { + _reply(res, JSON.stringify(request)); + break; + } + } +} + + +function query(words, res) { + LOG.debug("Request handler 'query' was called."); + + var request = {}; + + switch (words[2]) { + + // get subscribers of current node + case 'subscribers': { + LOG.debug('subscribers ...'); + // ensure this method doesn't get abused + + // extract parameters + request = { apikey: words[3] || '', + layer: words[4] || '', + ident: words[5] || '' + }; + + // check if node exists, return error if not yet exist (need to publishPos first) + var node = _getNode(request, res); + + if (typeof node === 'object') { + // get a list of current neighbors's id + var neighbors = node.list(); + var list = []; + + // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) + for (var id in neighbors) + list.push(id); + + // return success + _reply(res, list); + } + break; + } + default: { + _reply(res, JSON.stringify(request)); + break; + } + } +} + +// remove a node from system +function revoke(words, res) { + LOG.debug("Request handler 'revoke' was called."); + + var request = {}; + + switch (words[2]) { + + // get subscribers of current node + case 'node': { + LOG.debug('node ...'); + // ensure this method doesn't get abused + + // extract parameters + request = { apikey: words[3] || '', + layer: words[4] || '', + ident: words[5] || '' + }; + + _revokeNode(request, function(result) { + // return success + if (result === true) + _reply(res, ["OK", []]); + else + _reply(res, ["", ["revoke fail"]]); + }); + + break; + } + default: { + _reply(res, JSON.stringify(request)); + break; + } + } +} + + +exports.publish = publish; +exports.subscribe = subscribe; +exports.unsubscribe = unsubscribe; +exports.query = query; +exports.revoke = revoke; + diff --git a/VSS/router.js b/VSS/router.js new file mode 100644 index 00000000..3f3e1358 --- /dev/null +++ b/VSS/router.js @@ -0,0 +1,37 @@ + +/* + A router for VSS server to execute actual commands + +*/ + +function printWords(words) { + + var str = ''; + for (var i=0; i < words.length; i++) + str += i + ':' + words[i] + ' '; + + return str; +} + +function route(handle, pathname, res) { + LOG.debug("about to route a request for " + pathname); + + // extract first verb in path + var words = pathname.split("/"); + for (var i=0; i < words.length; i++) + LOG.debug(words[i]); + + var verb = words[1]; + LOG.debug('verb is: ' + verb); + + if (typeof handle[verb] === 'function') { + handle[verb](words, res); + } + else { + LOG.warn('no request handle for: ' + pathname); + res.writeHead(404, {'Content-Type': 'text/plain'}); + res.end('404 Not Found: ' + pathname); + } +} + +exports.route = route; \ No newline at end of file diff --git a/VSS/server.js b/VSS/server.js new file mode 100644 index 00000000..e6686ba0 --- /dev/null +++ b/VSS/server.js @@ -0,0 +1,32 @@ +/* + server.js + + Voronoi Spatial Publish (VSS) server + + + 2012.09.18 init + + +*/ + +var serverPort = 39900; + +var http = require('http'); +var urlParser = require('url'); + +// start server +var start = function (route, handle) { + + http.createServer(function (req, res) { + + // print out path + var obj = urlParser.parse(req.url, true); + + route(handle, obj.pathname, res); + + }).listen(serverPort); + + LOG.debug('Server running at http://127.0.0.1:' + serverPort + '/'); +} + +exports.start = start; \ No newline at end of file diff --git a/net_nodejs.js b/net_nodejs.js index a474da91..41ead57a 100644 --- a/net_nodejs.js +++ b/net_nodejs.js @@ -62,25 +62,32 @@ // all received messages are delivered to 'onReceive' // return port binded, or 0 to indicate error listen(port, onDone) + + // check if I'm a listening server + isServer() + + // check if a client socket is currently connected + isConnected() history: 2012-06-30 initial version (start from stretch) - + 2012-09-24 add is_connected() */ + require('./common.js'); var l_net = require('net'); // allow using network function net_nodejs(onReceive, onConnect, onDisconnect, onError) { + // flag to indicate connectedness + var _connected = false; + // a server object for listening to incoming connections var _server = undefined; // a client object for making connection var _client = undefined; - - // the IP & port of a host to be connected - //var _target = undefined; - + // check for connection validity and send to it) // ip_port is an object with 'host' and 'port' parameter this.connect = function (host_port) { @@ -103,11 +110,14 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // store remote address & port _client.host = host_port.host; - _client.port = host_port.port; + _client.port = host_port.port; + + _connected = true; - setupSocket(_client); if (typeof onConnect === 'function') onConnect(_client); + + setupSocket(_client); }); } @@ -118,7 +128,8 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { try { LOG.debug('disconnecting from ' + _client.host + ':' + _client.port); - _client.end(); + _connected = false; + _client.end(); } catch (e) { LOG.error('net_nodejs: disconnect error: ' + e); @@ -196,6 +207,12 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { this.isServer = function () { return (_server !== undefined); } + + // check if the socket is currently connected + this.isConnected = function () { + //LOG.debug('connected: ' + _connected); + return _connected; + } // setup a new usable socket with a socket handler //----------------------------------------- @@ -209,8 +226,8 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // this is important to allow messages be delivered immediately after sending socket.setNoDelay(true); - // flag to indicate socket is already disconnected - socket.disconnected = false; + // flag to indicate whether socket is connected + socket.connected = false; // call data callback to process data, if exists // (this happens when setupSocket is called by a listening server for setup new socket) @@ -226,6 +243,7 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { socket.resume(); }); + // NOTE: this won't be triggered for out-going connections, only incoming (i.e., when a listening server calls this) socket.on('connect', function () { // NOTE: remoteAddress & remotePort are available for incoming connection, but not outgoing sockets @@ -235,11 +253,11 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { socket.port = socket.remotePort; LOG.debug('connection created: ' + socket.host + ':' + socket.port); + socket.connected = true; + // notify connection, pass the connecting socket if (typeof onConnect === 'function') - onConnect(socket); - - socket.disconnected = false; + onConnect(socket); }); // handle connection error or close @@ -252,16 +270,16 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // if we've already fire disconnect for this socket, ignore it // NOTE: as both 'close' and 'end' event could cause this callback be called // so we need to prevent a 2nd firing of the disconnect callback to application - if (socket.disconnected === true) + if (socket.connected === false) return; LOG.debug('connection ended. remote host is ' + socket.host + ':' + socket.port); + + socket.connected = false; // notify application, if callback exists if (typeof onDisconnect === 'function') - onDisconnect(socket); - - socket.disconnected = true; + onDisconnect(socket); } // NOTE:: if remote host calls 'disconnect' to send a FIN packet, diff --git a/socket.io/socket.io.js b/vast.io/socket.io/socket.io.js similarity index 100% rename from socket.io/socket.io.js rename to vast.io/socket.io/socket.io.js diff --git a/socket.io/socket.io.min.js b/vast.io/socket.io/socket.io.min.js similarity index 100% rename from socket.io/socket.io.min.js rename to vast.io/socket.io/socket.io.min.js diff --git a/vast.io-client (local).html b/vast.io/vast.io-client (local).html similarity index 100% rename from vast.io-client (local).html rename to vast.io/vast.io-client (local).html diff --git a/vast.io-client (dev).html b/vast.io/vast.io-client.html similarity index 100% rename from vast.io-client (dev).html rename to vast.io/vast.io-client.html diff --git a/vast.io-server.js b/vast.io/vast.io-server.js similarity index 97% rename from vast.io-server.js rename to vast.io/vast.io-server.js index 787706d5..cadac370 100644 --- a/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -6,8 +6,7 @@ process.on('uncaughtException', function(err) { */ // include VAST -require('./common'); - +require('../common'); LOG.setLevel(2); /* @@ -157,7 +156,7 @@ io.sockets.on('connection', function (socket) { for (var i=0; i < edges.length; i++) edge_list.push({a: edges[i].va, b: edges[i].vb}); - LOG.debug('list sends back ' + neighbor_list.length + ' neighbors'); + //LOG.debug('list sends back ' + neighbor_list.length + ' neighbors and ' + edge_list.length + ' edges'); // return to client socket.emit('neighbors', {nodes: neighbor_list, edges:edge_list}); diff --git a/vast.io.bat b/vast.io/vast.io.bat similarity index 100% rename from vast.io.bat rename to vast.io/vast.io.bat diff --git a/vast.io.js b/vast.io/vast.io.js similarity index 100% rename from vast.io.js rename to vast.io/vast.io.js diff --git a/vast_net.js b/vast_net.js index 11b76575..6c10ebc7 100644 --- a/vast_net.js +++ b/vast_net.js @@ -65,9 +65,6 @@ require('./common.js'); var l_net = require('./net_nodejs'); // implementation-specific network layer -// an id that indicates unassignedment -//var VAST_ID_UNASSIGNED = 0; - // // input: // CB_receive(id, msg) callback when a message is received @@ -124,6 +121,9 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // TODO: simpler approach? (store just _conn and no _sockets?) var _sockets = {}; + // queue to store messages pending transmission after connection is made + var _msgqueue = {}; + // net_nodejs object for acting as server (& listen to port) var _server = undefined; @@ -180,20 +180,20 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { is_reliable = true; // actually sending the message (attaching '\n' to indicate termination) - var send_msg = function () { + var send_msg = function (message) { LOG.debug('[' + _self_id + '] send_msg to [' + id + ']: '); - LOG.debug(msg); + LOG.debug(message); // if I'm a server, send via one of the recorded sockets if (_server !== undefined && _sockets.hasOwnProperty(id)) { //LOG.debug('send to in conn [' + id + ']'); - _server.send(msg + '\n', _sockets[id]); + _server.send(message + '\n', _sockets[id]); } // if the target is available on an out-going channel else if (_conn.hasOwnProperty(id)) { //LOG.debug('send to out conn [' + id + ']'); - _conn[id].send(msg + '\n'); + _conn[id].send(message + '\n'); } // otherwise I should have a connection mapping else { @@ -201,12 +201,24 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { return false; } - return true; + return true; } // check if connections exist, if not then start to connect - if (_conn.hasOwnProperty(id) === true || _sockets.hasOwnProperty(id) === true) - return send_msg(); + if (_sockets.hasOwnProperty(id) === true) + return send_msg(msg); + + if (_conn.hasOwnProperty(id) === true) { + + // for outgoing connections, check if establish, if not then queue + if (_conn[id].isConnected() === true) + return send_msg(msg); + + // store message to queue + LOG.debug('storing msg to msgqueue [' + id + '] msg: ' + msg); + _msgqueue[id].push(msg); + return true; + } // check for id to address mapping if (_id2addr.hasOwnProperty(id) === false) { @@ -214,6 +226,10 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { return false; } + // create queue for new socket + _msgqueue[id] = []; + _msgqueue[id].push(msg); + // create a new socket _conn[id] = new l_net( @@ -222,11 +238,11 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // connect callback function (socket) { - LOG.debug('id: ' + id + ' connected'); + LOG.debug('[' + id + '] connected'); // store id to socket, so we can identify the source of received messages socket.id = id; - + // notify remote host of my id // TODO: replace with binary handshake (for efficiency) //_conn[id].send(_self_id + '\n'); @@ -239,8 +255,16 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { if (typeof CB_connect === 'function') CB_connect(id); - // send out message - send_msg(); + // send out pending messages + if (_msgqueue.hasOwnProperty(id)) { + var list = _msgqueue[id]; + LOG.debug('msgqueue size for [' + id + ']: ' + list.length); + for (var i=0; i < list.length; i++) + send_msg(list[i]); + + // clear queue + _msgqueue[id] = []; + } }, // disconnect callback @@ -349,6 +373,7 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { LOG.debug('disconnect outgoing socket for id: ' + id); _conn[id].disconnect(); delete _conn[id]; + delete _msgqueue[id]; } else { LOG.debug('cannot find id [' + id + '] to disconnect'); @@ -401,11 +426,11 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { if (idx === (-1)) break; - var result = socket.recv_buf.slice(0, idx); + var str = socket.recv_buf.slice(0, idx); // deliver this parsed data for processing - if (result !== undefined) { - //LOG.debug('result: ' + result + ' from id: ' + socket.id); + if (str !== undefined) { + //LOG.debug('str: ' + str + ' from id: ' + socket.id); // check if remote connection sends in its ID initially // NOTE: this check should be performed only once @@ -414,13 +439,14 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { if (socket.id < VAST_ID_UNASSIGNED) { // assume the first message is remote node's id - var remote_id = parseInt(result); + var remote_id = parseInt(str); LOG.debug('remote id: ' + remote_id); // skip message if remote_id is invalid // TODO: try to determine the cause & fix this + // NOTE: if this happens, the message is simply ignored if (isNaN(remote_id)) { - LOG.debug('[' + _self_id + '] remote_id isNaN, result: ' + result + ' from id: ' + socket.id); + LOG.error('[' + _self_id + '] remote_id is NaN, str: ' + str + ' from id: ' + socket.id); } else { @@ -448,14 +474,14 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // NOTE: we assume the first message received is new id else if (_self_id === VAST_ID_UNASSIGNED) { - var assigned_id = parseInt(result); + var assigned_id = parseInt(str); LOG.debug('assigned_id: ' + assigned_id); _self_id = assigned_id; } // otherwise is a real msg, notify custom callback of incoming data (if provided) else if (typeof CB_receive === 'function') { - //LOG.debug('recv from [' + socket.id + '] result: ' + result); - CB_receive(socket.id, result); + //LOG.debug('recv from [' + socket.id + '] str: ' + str); + CB_receive(socket.id, str); } } // update buffer diff --git a/vast_types.js b/vast_types.js index fd1ff072..953e90ca 100644 --- a/vast_types.js +++ b/vast_types.js @@ -88,7 +88,9 @@ var l_area = exports.area = function (center, radius) { if (radius === undefined) radius = 0; - this.center = center; + // store center & radius, with potential error checking + this.center = new l_pos(0, 0); + this.center.parse(center); this.radius = radius; this.equals = function (other_area) { From 4e6d5be48373671ebebe5c8e5bdfca5a8a3adeb4 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Sat, 29 Sep 2012 09:23:53 +0800 Subject: [PATCH 003/141] get subscribers returns correct ident --- VAST WorkLog.txt | 27 ++++++++++++++++++++++++--- VSS/handler.js | 34 ++++++++++++++++++++++++++++++---- vast_types.js | 5 +++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index b16d1fdf..0de96840 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -21,7 +21,8 @@ BUG (*) denotes design bug: 2012-08-29: scaling many nodes will cause TCP connection errors 2012-09-03: EADDRINUSE crash bug (after running a while with >50 nodes) 2012-09-24: send_msg in vast_net does not necessarily send out in FIFO order - +2012-09-29a: EPERM crashing error when other nodes shutdown with Ctrl-C (unexpectedly) +2012-09-29b: VON topology not updated when VSS/vast.io server shutdowns ---- @@ -57,14 +58,34 @@ debug VON peer usage from VSS server allow ident be queried when get subscribers +2012-09-29 (6) +-------------- +goal: ident discovery among neighbors + limit get subscriber result within AOI + +- ident discovery & AOI limiting done + +BUG: when VSS server or vast.io server shutdown, it seems like the VON topology is not + updated accordingly (correctly), and ghost nodes are still left on the system + (disconnection not handled correctly?) + +BUG: when VSS server shutdowns sometimes other still existing nodes (gateway) + will get EPERM error and crash + + + 2012-09-28 (5) -------------- goal: debug & test JSON response debug node register / revoke - - +MILESTONE +- fix response format to JSON and deploy to dev server +- functions still missing: + need to support neighbor node's ident discovery + need to respond neighbor list whenever doing publishPos + 2012-09-27 (4) -------------- goal: debug & test JSON response diff --git a/VSS/handler.js b/VSS/handler.js index ac4d0ab3..2b289b6b 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -20,6 +20,10 @@ var _nodes_created = 0; // NOTE: it's a 3-dimensional array var _keys = {}; +// node id -> ident mapping +var _id2ident = {}; + + // // actual execution code // @@ -63,7 +67,11 @@ var _registerNode = function (pos, info, done_CB) { _keys[info.apikey][info.layer] = {}; _keys[info.apikey][info.layer][info.ident] = new_node; - + + // store id to ident mapping + LOG.debug('store mapping for node id: ' + self_id + ' ident: ' + info.ident); + _id2ident[self_id] = info.ident; + // check content LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); @@ -79,7 +87,9 @@ var _revokeNode = function (info, done_CB) { if (node === undefined || node === null) return done_CB(false); - + + var node_id = node.getSelf().id; + //destroy node node.leave(); node = null; @@ -91,6 +101,10 @@ var _revokeNode = function (info, done_CB) { if (Object.keys(_keys[info.apikey]).length === 0) delete _keys[info.apikey]; + // remove id to ident mapping + LOG.debug('remove mapping for node id: ' + node_id + ' ident: ' + info.ident); + delete _id2ident[node_id]; + done_CB(true); } @@ -151,6 +165,14 @@ var _getNode = function (req, res) { return null; } +// check if a given node is a direct area subscriber for a center point +var _isSubscriber = function (node, aoi) { + + var result = node.aoi.covers(aoi.center); + LOG.debug('check if node [' + node.toString() + '] covers ' + aoi.center.toString() + ': ' + result); + return result; +} + function publish(words, res) { LOG.debug("Request handler 'publish' was called."); @@ -336,8 +358,12 @@ function query(words, res) { var list = []; // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) - for (var id in neighbors) - list.push(id); + for (var id in neighbors) { + // convert node id to node ident (only for those registered here) + // NOTE: current approach can only do ident translation for nodes created via this VSS server + if (_id2ident.hasOwnProperty(id) && _isSubscriber(neighbors[id], node.getSelf().aoi)) + list.push(_id2ident[id]); + } // return success _reply(res, list); diff --git a/vast_types.js b/vast_types.js index 953e90ca..4559e6e9 100644 --- a/vast_types.js +++ b/vast_types.js @@ -93,6 +93,11 @@ var l_area = exports.area = function (center, radius) { this.center.parse(center); this.radius = radius; + this.covers = function (pos) { + // NOTE: current check allows nodes even at AOI boundary to be considered as covered + return (this.center.distance(pos) <= this.radius); + } + this.equals = function (other_area) { return (this.center.equals(other_area.center) && this.radius === other_area.radius); } From 088031a580a02732585b138a2f950331e052d47b Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Sun, 30 Sep 2012 16:10:39 +0800 Subject: [PATCH 004/141] catch exception during VONpeer tick --- VAST WorkLog.txt | 7 +++++++ VON_peer.js | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 0de96840..16e9845b 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -57,6 +57,13 @@ connect VSS functions with VON peer debug VON peer usage from VSS server allow ident be queried when get subscribers +2012-09-30 (7) +-------------- +goal: disconnection handling when nodes abnormally depart + + + + 2012-09-29 (6) -------------- diff --git a/VON_peer.js b/VON_peer.js index afb229af..c72d160f 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -512,9 +512,14 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var _tick = function () { //_sendKeepAlive(); - _contactNewNeighbors(); - _checkNeighborDiscovery(); - _removeNonOverlapped(); + try { + _contactNewNeighbors(); + _checkNeighborDiscovery(); + _removeNonOverlapped(); + } + catch (e) { + LOG.error('tick error: ' + e); + } } // set current node to be 'joined' From 860de2d382bc4fa03c40144d621461597f0dcb92 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Sun, 30 Sep 2012 17:52:21 +0800 Subject: [PATCH 005/141] fixed disconnect issues so now it's handled properly --- VAST WorkLog.txt | 11 ++++++ net_nodejs.js | 88 ++++++++++++++++++++++++++---------------------- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 16e9845b..47130a99 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -61,9 +61,20 @@ allow ident be queried when get subscribers -------------- goal: disconnection handling when nodes abnormally depart +- fix connect/disconnect handling in net_nodejs.js +DEBUG when a 50 node simulation process shutdowns, the gateway crashes with ECONNREFUSED error + > fixed by catching 'error' event when making out-going connections +DEBUG after 50 node simu ends, the gateway can no longer accepts new clients, + as gateway still thinks those already disconnected peers exist.. + appearantly disconnection is not handled correctly + > fixed by re-ordering when disconnect_handler is called + (should be called in 'close' not 'end') +MILESTONE: + can now run 50 nodes, and shutdown 50 nodes (then re-join) without problems. + 2012-09-29 (6) -------------- diff --git a/net_nodejs.js b/net_nodejs.js index 41ead57a..c17043db 100644 --- a/net_nodejs.js +++ b/net_nodejs.js @@ -79,9 +79,6 @@ var l_net = require('net'); // allow using network function net_nodejs(onReceive, onConnect, onDisconnect, onError) { - // flag to indicate connectedness - var _connected = false; - // a server object for listening to incoming connections var _server = undefined; @@ -96,29 +93,37 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { try { // connect to the given host & port, while providing a connection listener - _client = l_net.createConnection(host_port.port, host_port.host); + _client = l_net.createConnection(host_port.port, host_port.host); + + _client.on('connect', function () { + + LOG.debug('out connect success for: ' + host_port.host + ':' + host_port.port); + + // store remote address & port + _client.host = host_port.host; + _client.port = host_port.port; + _client.connected = true; + + if (typeof onConnect === 'function') + onConnect(_client); + + // NOTE: the handler for 'connect' inside setupSocket won't be called, + // as 'connect' event is already triggered here, so whatever task needs to be duplicated here + // TODO: more elegant approach? + setupSocket(_client); + }); + + // if there's error on the connection, 'close' will follow + _client.on('error', function(err){ + LOG.error('out connect socket error: ' + err); + }); + } catch (e) { LOG.error('net_nodejs: connect error: ' + e); if (typeof onError === 'function') onError('connect'); - } - - _client.on('connect', function () { - - LOG.debug('out connect success for: ' + host_port.host + ':' + host_port.port); - - // store remote address & port - _client.host = host_port.host; - _client.port = host_port.port; - - _connected = true; - - if (typeof onConnect === 'function') - onConnect(_client); - - setupSocket(_client); - }); + } } // disconnect from a given socket id @@ -128,7 +133,6 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { try { LOG.debug('disconnecting from ' + _client.host + ':' + _client.port); - _connected = false; _client.end(); } catch (e) { @@ -181,12 +185,14 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { _server = l_net.createServer(setupSocket); - // attempt to re-try if bind fail + // pass back error for further processing _server.on('error', function (e) { - LOG.error('net_nodejs: listen error caught: ' + e); + //LOG.error('net_nodejs: listen error caught: ' + e); + //if (typeof onError === 'function') + // onError(e); if (typeof onDone === 'function') - onDone(0); + onDone(0); }); _server.listen(port, function () { @@ -210,8 +216,8 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // check if the socket is currently connected this.isConnected = function () { - //LOG.debug('connected: ' + _connected); - return _connected; + //LOG.warn('connected: ' + _client.connected); + return (_client !== undefined && _client.connected === true); } // setup a new usable socket with a socket handler @@ -226,9 +232,6 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // this is important to allow messages be delivered immediately after sending socket.setNoDelay(true); - // flag to indicate whether socket is connected - socket.connected = false; - // call data callback to process data, if exists // (this happens when setupSocket is called by a listening server for setup new socket) if (typeof onReceive === 'function') { @@ -257,19 +260,16 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // notify connection, pass the connecting socket if (typeof onConnect === 'function') - onConnect(socket); + onConnect(socket); }); // handle connection error or close - var disconnect_handler = function (has_error) { - - // print error, if any - if (has_error) - LOG.error('socket closed due to error'); - + var disconnect_handler = function () { + // if we've already fire disconnect for this socket, ignore it // NOTE: as both 'close' and 'end' event could cause this callback be called // so we need to prevent a 2nd firing of the disconnect callback to application + //LOG.debug('disconnect_handler, socket.connected: ' + socket.connected); if (socket.connected === false) return; @@ -283,10 +283,11 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { } // NOTE:: if remote host calls 'disconnect' to send a FIN packet, - // this host will receive 'end' + // this host will receive 'end' directly (without getting a 'close' event) // see: http://nodejs.org/api/net.html#net_event_close_1 socket.on('end', function () { - LOG.debug('connection closed by remote host'); + //LOG.warn('socket [end] called'); + // NOTE: should not end connection here, as default behavior would close it // this allows still some messages to be sent over by this host, // even if remote hosts closes the connection @@ -294,7 +295,14 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { }); // NOTE: this is called if an existing connection is fully closed (by itself or remote host) - socket.on('close', disconnect_handler); + socket.on('close', function (has_error) { + //LOG.warn('socket [close] called'); + // print error, if any + if (has_error) + LOG.error('socket close error: ' + has_error); + + disconnect_handler(); + }); // if there's error on the connection, 'close' will follow socket.on('error', function(err){ From 38f013d4e986af69a4e89a5dc85dc5eb3cae82f6 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Mon, 1 Oct 2012 17:17:14 +0800 Subject: [PATCH 006/141] modify VSS server publish/pos response to be query subscribers --- VAST WorkLog.txt | 15 +++++++++---- VSS/VSS.js | 2 +- VSS/handler.js | 57 +++++++++++++++++++++++++++++------------------- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 47130a99..dacc4d1a 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -52,10 +52,17 @@ BUG (*) denotes design bug: Overall Goals: extract non-VON-specific code from VON_peer as common code for handlers -build VSS server (to take & respond RESTful pub/sub commands) -connect VSS functions with VON peer -debug VON peer usage from VSS server -allow ident be queried when get subscribers + + +2012-10-01 (1) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg handlers + identify logical separation for matcher/relay/client/VON + +- modify VSS server response for publishPos as subscriber list + +BUG 50 node simu still crashes wtih error: EMFILE, EFAULT + when nodes are suddenly terminated (under windows) 2012-09-30 (7) -------------- diff --git a/VSS/VSS.js b/VSS/VSS.js index b908130b..010fb9d5 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -1,7 +1,7 @@ // include VAST require('../common'); -LOG.setLevel(3); +LOG.setLevel(2); var server = require("./server"); var router = require("./router"); diff --git a/VSS/handler.js b/VSS/handler.js index 2b289b6b..124d9476 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -125,6 +125,31 @@ var _reply = function (res, res_obj) { } } +// send back subscriber list to client +var _replySubscribers = function (request, res) { + + var node = _getNode(request, res); + + if (typeof node === 'object') { + + // get a list of current neighbors's id + var neighbors = node.list(); + var list = []; + + // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) + for (var id in neighbors) { + // convert node id to node ident (only for those registered here) + // NOTE: current approach can only do ident translation for nodes created via this VSS server + if (_id2ident.hasOwnProperty(id) && _isSubscriber(neighbors[id], node.getSelf().aoi)) + list.push(_id2ident[id]); + } + + // return success + _reply(res, list); + } +} + + // get a specific node given its API key, layer, and node ident // returns 'undefined' if key info is missing // returns 'null' if not found @@ -221,7 +246,9 @@ function publish(words, res) { LOG.debug('new_node: ' + JSON.stringify(new_node)); // send back node creation response - _reply(res, ["OK", []]); + //_reply(res, ["OK", []]); + + _replySubscribers(request, res); }); break; } @@ -229,7 +256,9 @@ function publish(words, res) { // publish pos default: { _publishPos(node, request, node.getSelf().aoi.radius); - _reply(res, ["OK", []]); + + //_reply(res, ["OK", []]); + _replySubscribers(request, res); break; } } @@ -350,26 +379,10 @@ function query(words, res) { }; // check if node exists, return error if not yet exist (need to publishPos first) - var node = _getNode(request, res); - - if (typeof node === 'object') { - // get a list of current neighbors's id - var neighbors = node.list(); - var list = []; - - // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) - for (var id in neighbors) { - // convert node id to node ident (only for those registered here) - // NOTE: current approach can only do ident translation for nodes created via this VSS server - if (_id2ident.hasOwnProperty(id) && _isSubscriber(neighbors[id], node.getSelf().aoi)) - list.push(_id2ident[id]); - } - - // return success - _reply(res, list); - } - break; - } + _replySubscribers(request, res); + + break; + } default: { _reply(res, JSON.stringify(request)); break; From ce727a121868b9db6361b7ac517900689f9692ed Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 25 Oct 2012 08:11:40 +0800 Subject: [PATCH 007/141] VSS response fixed, adopt msg_handler with VON_peer --- VAST WorkLog.txt | 182 +++++++++++++++++++++++ VAST.js architecture.txt | 77 ++++++++++ VON_peer.js | 168 ++++++++++----------- VSS/VSS.js | 2 +- VSS/handler.js | 29 +++- VSS/server.js | 2 +- common.js | 10 +- common/logger.js | 2 +- net/msg_handler.js | 176 ++++++++++++++++++++++ net_nodejs.js => net/net_nodejs.js | 6 +- test_vast_net.js => net/test_vast_net.js | 15 +- vast_net.js => net/vast_net.js | 23 +-- test_VON_peer.js | 8 +- test_inhert.js | 55 +++++++ vast.io/vast.io-server.js | 2 +- 15 files changed, 638 insertions(+), 119 deletions(-) create mode 100644 VAST.js architecture.txt create mode 100644 net/msg_handler.js rename net_nodejs.js => net/net_nodejs.js (98%) rename test_vast_net.js => net/test_vast_net.js (83%) rename vast_net.js => net/vast_net.js (96%) create mode 100644 test_inhert.js diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index dacc4d1a..05b5182a 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -54,11 +54,193 @@ Overall Goals: extract non-VON-specific code from VON_peer as common code for handlers + +2012-10-25 (4) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + make multiple handlers work with same network layer + modify VSS response + + + + + +2012-10-24 (3) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + make multiple handlers work with same network layer + modify VSS response + debug msg_handler mechanism (only one 'net' instance) + +DEBUG: 2012-10-23 + > bug solved by making sure we don't use any variable within the parent class, + all variables in a prototype-inherted parent have only one instance and are shared + so we need to make sure instance-specific variables can all be independent + this is resolved by passing instance-specific functions via the parent class's constructor + + +2012-10-23 (2) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + make multiple handlers work with same network layer + modify VSS response + debug msg_handler mechanism (only one 'net' instance) + +- found out one problem is that by using 'prototype', only one copy of the variable + exists, thus causing problems if multiple instances may access the variable. + solution is to make the variable 'instance-based' (making it 'this.xxx') + but then it may not be accessible from callback functions (scope problem) + +- after applying right context (using 'apply') seems to work with prototype + +BUG 2nd node via VSS cannot properly join & obtain VON_NODE response from gateway + appearantly gateway forward VON_QUERY to not an existing node, but the new node + + + +2012-10-22 (1) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + make multiple handlers work with same network layer + modify VSS response + debug msg_handler mechanism (_net not found) + +- modify all public methods & variable in msg_handler to adopt 'prototype'-style definition + can run, but 'net' reference is still shared by ALL created msg_handler instances + (desired behavior is that each msg_handler instance has its own 'network' layer) + +2012-10-21 (7) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + make multiple handlers work with same network layer + modify VSS response + +BUG: _net reference is not found (when VON_peer called a 2nd time) + while creating VON_peer via the VSS server + + + +2012-10-20 (6) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + modify VSS response + debug using msg_handler class (js inhertance work) + +- modify to pass packetHandler as part of the init() parameter to parent class + > solve the issue of parent class cannot call override function in child class + (issue found on 2012-10-17). + + + +2012-10-17 (3) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + modify VSS response + debug using msg_handler class (js inhertance work) + +- tested out using msg_handler successfully, in progress of converting +- found out an issue: parent class cannot call / use methods override by child class + +2012-10-14 (7) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + modify VSS response + debug using msg_handler class (js inhertance work) + +- msg_handler inhertance works under test_vast_net + + +2012-10-13 (6) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + debug using msg_handler class (js inhertance work) + identify why inhertance doesn't work + +- made sample code inhertance work with prototype + +2012-10-12 (5) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + debug using msg_handler class (js inhertance work) + +- verified js inhertance indeed work in small test program + + +2012-10-11 (4) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + debug using msg_handler class (js inhertance work) + +- will try base = prototype approach (suggested by Shine) + did not seem to work... + +2012-10-09 (2) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + debug using msg_handler class (js inhertance work) + +- study js inhertance + +2012-10-08 (1) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + debug using msg_handler class (js inhertance work) + +- study js inhertance + +2012-10-07 (7) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + debug using msg_handler class + +- study javascript inhertance approaches + +2012-10-06 (6) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + debug /net directory layout & using msg_handler class + +- done test_vast_net debug + + +2012-10-05 (5) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + +- start moving network code to /net directory (will crash) + + +2012-10-04 (4) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for msg_handler + +- init order in VAST C++ + - create net + - create & join relay + - create & join matcher + - init done + + ordering, dependency, consistency + + +2012-10-03 (3) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + +- start making msg_handler.js class + +2012-10-02 (2) +-------------- +goal: extract non-VON-specific code from VON_peer as common code for handlers + +- still stuck & consider how to do module separation + 2012-10-01 (1) -------------- goal: extract non-VON-specific code from VON_peer as common code for msg handlers identify logical separation for matcher/relay/client/VON +- recheck 9-14 & 9-11 notes - modify VSS server response for publishPos as subscriber list BUG 50 node simu still crashes wtih error: EMFILE, EFAULT diff --git a/VAST.js architecture.txt b/VAST.js architecture.txt new file mode 100644 index 00000000..e72fde5c --- /dev/null +++ b/VAST.js architecture.txt @@ -0,0 +1,77 @@ + + +Main Functions +============== + +VON: peer connectivity, neighbor discovery within peer's AOI + - find gateway and join network + - position movement + - update from neighbors + + assumes: + - net_layer (send/recv) + + implemented by: + - VON_peer + +SPS: pub/sub record keeping, pub/sub matching, request passing + - pub/sub request + - sub record storage + - pub request passing to neighbors + - pub/sub matching + - delivery of messages + + assumes: + - net_layer (send/recv) + - region + - neighboring region + + implemented by: + - VASTMatcher + - VASTClient + + +VSO: region resizing, pub/sub record migration, fault-tolerance (?) + - load detection + - overload handling (resize) + - overload handling (join) + - underload handling (resize) + - underload handling (leave) + - load migration (ensure consistency & fault-tolerance) + +VCAST: request forwarding + + +Dependencies +============ +VSO -> VON +VCAST -> VON/VSO +SPS -> VON/VSO/VCAST + + +Modular Work Division +===================== +VASTMatcher: + support for pub/sub matching should exist at VASTMatcher + it deals with pub/sub record keeping & matching + VASTMatcher can be implemented with either VON or VSO, + the latter will provide dynamic load balancing among matchers. + if only the former is used, then only static partitioning is available. + + basic matcher functions: + * accept subscribe request (from VASTClient) + * accept publish request (from VASTClient) + * forward publish request to neighbors + * deliver publications to relevant subscribers within own region + * list currently known subscribers + * join a VON network and maintain topology with neighbors + + +VASTRelay: + support physical coordniate identification and connection to closest Relay + +VASTClient: + basic client functions to forward all requests to current "owner matcher" + and get all responses from "owner matcher" (possibly via Relay) + + diff --git a/VON_peer.js b/VON_peer.js index c72d160f..3d663183 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -59,6 +59,10 @@ */ require('./common.js'); + +// to be inherited by VON.peer +var msg_handler = msg_handler || require('./net/msg_handler.js'); + var Voronoi = require('./vast_voro.js'); // config @@ -102,6 +106,7 @@ var VON_Message_String = [ 'VON_BYE' ]; +// TODO: msg priority is probably not VON-specific var VON_Priority = { HIGHEST: 0, HIGH: 1, @@ -110,6 +115,7 @@ var VON_Priority = { LOWEST: 4 }; +// TODO: node state is probably not VON-specific var NodeState = { ABSENT: 0, QUERYING: 1, // finding / determing certain thing @@ -132,10 +138,9 @@ var NeighborState = { NEIGHBOR_ENCLOSED: 2 }; - // definition of a VON peer -exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { - +function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { + ///////////////////// // public methods // @@ -164,7 +169,9 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // set self AOI _self.aoi.update(aoi); - + + LOG.debug('calling getHost()'); + // create self object _net.getHost(function (local_IP) { @@ -540,7 +547,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // simply move a little to indicate keepalive if (_isJoined() && _isTimelyNeighbor (_self.id, MAX_TIMELY_PERIOD/2) == false) { LOG.debug('[' + _self.id + '] sendKeepAlive ()', _self.id); - move (_self.aoi); + move(_self.aoi); } } @@ -802,34 +809,6 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // send helpers // - // send a javascript object to a SINGLE target node (given message 'type' and 'priority') - var _sendMessage = function (target, msg_type, js_obj, priority, is_reliable) { - - // create a delivery package to send - var pack = new VAST.pack( - msg_type, - js_obj, - priority); - - pack.targets.push(target); - - // convert pack to JSON or binary string - return _sendPack(pack, is_reliable); - } - - // send a pack object to its destinated targets - // TODO: this previously exist at the network layer, should move it there? - var _sendPack = function (pack, is_reliable) { - - // serialize string - var encode_str = JSON.stringify(pack); - - // go through each target and send - // TODO: optimize so only one message is sent to each physical host target - for (var i=0; i < pack.targets.length; i++) - _net.send(pack.targets[i], encode_str, is_reliable); - } - // send node infos (NODE message) to a particular target // target: destination node id // list: a list of neighbor id to be sent @@ -898,36 +877,16 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // handlers for incoming messages, connect/disconnect // - // handler for incoming messages - var _msgHandler = function (from_id, msg) { - - LOG.debug('[' + _self.id + '] handles msg from [' + from_id + '] msg: ' + msg); - - // prevent processing invalid msg - // NOTE: we allow for empty message (?) such as VON_DISCONNECT - if (msg == '' || msg === null || msg === undefined) { - LOG.error('[' + _self.id + '] msgHandler got invalid msg, skip processing'); - return; - } + var _handlePacket = function (from_id, pack) { - var pack; - try { - // convert msg back to js_obj - pack = JSON.parse(msg); - } - catch (e) { - LOG.error('convert to js_obj fail: ' + e + ' msg: ' + msg); - return; - } - - LOG.debug('[' + _self.id + '] ' + VON_Message_String[pack.type] + ' from [' + from_id + '], neighbor size: ' + Object.keys(_neighbors).length); - // if join is not even initiated, do not process any message if (_state == NodeState.ABSENT) { LOG.error('node not yet join, should not process any messages'); - return; + return false; } - + + LOG.debug('[' + _self.id + '] ' + VON_Message_String[pack.type] + ' from [' + from_id + '], neighbor size: ' + Object.keys(_neighbors).length); + switch (pack.type) { // VON's query, to find an acceptor that can take in a joining node @@ -949,7 +908,9 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { } // find the node closest to the joiner - var closest = _voro.closest_to (joiner.aoi.center); + var closest = _voro.closest_to(joiner.aoi.center); + + LOG.debug('closest node found: ' + closest); // if this is gateway receiving its own request if (closest === VAST_ID_UNASSIGNED && Object.keys(_neighbors).length === 1) { @@ -1018,6 +979,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // if VON_NODE is received, we're considered joined // NOTE: do this first as we need to update our self ID for later VON_NODE process to work + LOG.warn('checking joining state: ' + _state); if (_state === NodeState.JOINING) { // check if we're getting new ID @@ -1092,7 +1054,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _sendMessage(from_id, VON_Message.VON_HELLO_R, pos, VON_Priority.HIGH, true); // check if enclosing neighbors need any update - _checkConsistency (from_id); + _checkConsistency(from_id); } break; @@ -1107,7 +1069,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { neighbor.aoi.center.parse(pack.msg); LOG.debug('got latest pos: ' + neighbor.aoi.center.toString() + ' id: ' + from_id); - _insertNode (neighbor); + _insertNode(neighbor); delete _potential_neighbors[from_id]; } @@ -1223,30 +1185,16 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { } break; - default: { - LOG.error('cannot recongize message type: ' + pack.type + ' msg: ' + msg); - } + default: + // packet unhandled + return false; break; } - } - - var _connHandler = function (id) { - LOG.debug('VON_peer [' + id + '] connected'); - } - - var _disconnHandler = function (id) { - LOG.debug('VON_peer [' + id + '] disconnected'); - - // generate a VON_DISCONNECT message - var pack = new VAST.pack( - VON_Message.VON_DISCONNECT, - {}, - VON_Priority.HIGHEST); - var encode_str = JSON.stringify(pack); - _msgHandler(id, encode_str); + // successfully handle packet + return true; } - + // clean up all internal states for a new fresh join var _initStates = function () { @@ -1274,13 +1222,53 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // init stat collection for selected messsage types _stat = {}; - _stat[VON_Message.VON_NODE] = new VAST.ratio(); + _stat[VON_Message.VON_NODE] = new VAST.ratio(); + } + + var _connHandler = function (id) { + LOG.debug('VON peer [' + id + '] connected'); } + + var _disconnHandler = function (id) { + LOG.debug('VON peer [' + id + '] disconnected'); + + // generate a VON_DISCONNECT message + var pack = new VAST.pack( + VON_Message.VON_DISCONNECT, + {}, + VON_Priority.HIGHEST); + + _handlePacket(id, pack); + } ///////////////////// // constructor // - + LOG.debug('VON_peer constructor called'); + + // call parent class's constructor + //this.init(_connHandler, _disconnHandler, _handlePacket, l_self_id); + msg_handler.call(this, _connHandler, _disconnHandler, _handlePacket, l_self_id); + + // make a local reference to the parent class (msg_handler)'s net object + // need to create reference here because within functions (such as callbacks) + // 'this' refers to the function itself + + // TODO: a cleaner approach? + var _net = this.net; + var that = this; + + // convenience functions with the right executation context (the desirable 'this') + var _sendMessage = function () { + that.sendMessage.apply(that, arguments); + } + + var _sendPack = function () { + that.sendPack.apply(that, arguments); + } + + LOG.warn('_net: ' + _net + ' this.net: ' + this.net); + // set default AOI buffer size if (l_aoi_buffer === undefined) l_aoi_buffer = AOI_DETECTION_BUFFER; @@ -1292,11 +1280,7 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { if (l_port === undefined) l_port = VON_DEFAULT_PORT; - - // create network layer & start listening - // NOTE: internal handlers must be defined before creating the VAST.net instance - var _net = new VAST.net(_msgHandler, _connHandler, _disconnHandler, l_self_id); - + // reference to self // NOTE: other info of 'self' may be empty at this moment (e.g., endpt, aoi, etc.) var _self = new VAST.node(l_self_id); @@ -1337,3 +1321,11 @@ exports.peer = function (l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _initStates(); } // end of peer + +VONPeer.prototype = new msg_handler(); + +// NOTE: it's important to export VONPeer after the prototype declaration +// otherwise the exported method will not have unique msg_handler instance +// (that is, all msg_handler variables will become singleton, only one instance globally) +if (typeof module !== 'undefined') + module.exports = VONPeer; diff --git a/VSS/VSS.js b/VSS/VSS.js index 010fb9d5..b908130b 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -1,7 +1,7 @@ // include VAST require('../common'); -LOG.setLevel(2); +LOG.setLevel(3); var server = require("./server"); var router = require("./router"); diff --git a/VSS/handler.js b/VSS/handler.js index 124d9476..4fab4b16 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -70,10 +70,15 @@ var _registerNode = function (pos, info, done_CB) { // store id to ident mapping LOG.debug('store mapping for node id: ' + self_id + ' ident: ' + info.ident); - _id2ident[self_id] = info.ident; + _id2ident[self_id] = info; // check content LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); + + // perform initial publish pos + // (so we can get a list of neighbors) + // NOTE: doesn't appear to work... + //_publishPos(new_node, info, new_node.getSelf().aoi.radius); done_CB(new_node); } @@ -136,16 +141,30 @@ var _replySubscribers = function (request, res) { var neighbors = node.list(); var list = []; + var self = node.getSelf(); + // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) for (var id in neighbors) { // convert node id to node ident (only for those registered here) // NOTE: current approach can only do ident translation for nodes created via this VSS server - if (_id2ident.hasOwnProperty(id) && _isSubscriber(neighbors[id], node.getSelf().aoi)) - list.push(_id2ident[id]); + + LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); + // do not return if: + // 1. is self + // 2. no mapping for ident + // 3. is not a subscriber to myself (subscribed area does not cover me) + if (self.id == id || + _id2ident.hasOwnProperty(id) === false || + _isSubscriber(neighbors[id], self.aoi) === false) + continue; + + var info = _id2ident[id]; + list.push(info.apikey + ':' + info.layer + ':' + info.ident); } // return success - _reply(res, list); + var error = (list.length === 0 ? ["no subscribers"] : []); + _reply(res, [list, error]); } } @@ -247,7 +266,7 @@ function publish(words, res) { // send back node creation response //_reply(res, ["OK", []]); - + _replySubscribers(request, res); }); break; diff --git a/VSS/server.js b/VSS/server.js index e6686ba0..09490a84 100644 --- a/VSS/server.js +++ b/VSS/server.js @@ -26,7 +26,7 @@ var start = function (route, handle) { }).listen(serverPort); - LOG.debug('Server running at http://127.0.0.1:' + serverPort + '/'); + LOG.warn('Server running at http://127.0.0.1:' + serverPort + '/'); } exports.start = start; \ No newline at end of file diff --git a/common.js b/common.js index 43ef4233..aef42bf4 100644 --- a/common.js +++ b/common.js @@ -14,10 +14,12 @@ global.UTIL = require('./common/util'); // // VAST & VON // -global.VAST = require('./vast_types'); -global.VAST.net = require('./vast_net'); -global.VON = require('./VON_peer'); - // ID definitions global.VAST_ID_GATEWAY = 1; global.VAST_ID_UNASSIGNED = 0; + +global.VAST = require('./vast_types'); +global.VAST.net = require('./net/vast_net'); +global.VON = { + peer: require('./VON_peer') +}; diff --git a/common/logger.js b/common/logger.js index 2ee01349..99ad35b5 100644 --- a/common/logger.js +++ b/common/logger.js @@ -15,7 +15,7 @@ var _yellow = '\033[33m'; var _ERR = _red + 'ERR -'; var _ERREND = _white; -var _WARN = _yellow + 'WARN -'; +var _WARN = _yellow + '-'; function logger() { diff --git a/net/msg_handler.js b/net/msg_handler.js new file mode 100644 index 00000000..1b7a8fe6 --- /dev/null +++ b/net/msg_handler.js @@ -0,0 +1,176 @@ + +/* + * VAST, a scalable peer-to-peer network for virtual environments + * Copyright (C) 2005-2011 Shun-Yun Hu (syhu@ieee.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + msg_handler.js + + a simple class to dispatch incoming messages to registered handlers, + while translating internal objects to outgoing message types (in JSON format) + + basic goals: + - allow multiple instances of the message handler be created, each responsible for a different logic + - allow a single network layer be used by multiple handlers + - ease of development (handle writers need not know each other, can be independently plug in & out) + - automatic dispatching to the right handler for message handling + + supported functions: + + // basic callback / structure + sendMessage + sendPack + _msgHandler + + // constructor + + // basic functions + + // accessors + + history: + 2012-10-03 initial version +*/ + +function msg_handler(l_connHandler, l_disconnHandler, l_packetHandler, l_self_id) { + + // prevent incorrect init + if (l_connHandler === undefined || + l_disconnHandler === undefined || + l_packetHandler === undefined) + return; + + // + // public methods (usable by classes that inherent the msg_handler class) + // + + // + // private methods (internal usage, some are replacable at inherted class) + // + + // constructor + LOG.debug('msg_handler init, l_packetHandler: ' + typeof l_packetHandler); + + // NOTE: packet & message handlers cannot be of prototype-style, + // as they need to occupy memory independently for each msg handler instance + + // default packet handler (does not process anything) + //var _handlePacket = undefined; + + // keep local reference for 'this' + var _that = this; + + // handler for incoming messages + this.msgHandler = function (from_id, msg) { + + // prevent processing invalid msg + // NOTE: we allow for empty message (?) such as VON_DISCONNECT + if (msg == '' || msg === null || msg === undefined) { + LOG.error('msgHandler: invalid msg from [' + from_id + '], skip processing'); + return; + } + + var pack; + + try { + // convert msg back to js_obj + pack = JSON.parse(msg); + } + catch (e) { + LOG.error('msgHandler: convert to js_obj fail: ' + e + ' msg: ' + msg); + return; + } + + if (l_packetHandler === undefined) { + LOG.error('no valid packet handler for type: ' + pack.type + ' msg: ' + msg); + return; + } + + //LOG.debug('call packetHandler: ' + typeof l_packetHandler); + if (l_packetHandler(from_id, pack) === false) + LOG.error('cannot recongize message type: ' + pack.type + ' msg: ' + msg); + } + + /* + // initialize message handler + this.init = function (connHandler, disconnHandler, packetHandler, self_id) { + + // replace default with provided custom handler, if any + if (typeof packetHandler === 'function') { + LOG.warn('handlePacket type is: ' + typeof _handlePacket); + _handlePacket = packetHandler; + } + + LOG.debug('msg_handler:init() called. checking existence of net object...'); + + // create new net layer if does not exist + if (this.net === undefined) { + LOG.warn('creating new VASTnet...'); + + // create network layer & start listening + // NOTE: internal handlers must be defined before creating the VAST.net instance + this.net = new VAST.net(this.msgHandler, connHandler, disconnHandler, self_id); + } + } + */ + + // create new net layer if does not exist + LOG.warn('creating new VASTnet... net: ' + typeof this.net); + + // create network layer & start listening + // NOTE: internal handlers must be defined before creating the VAST.net instance + this.net = new VAST.net(this.msgHandler, l_connHandler, l_disconnHandler, l_self_id); + + +} // end msg_handler + + +// send a pack object to its destinated targets +// TODO: this previously exist at the network layer, should move it there? +msg_handler.prototype.sendPack = function (pack, is_reliable) { + + // serialize string + var encode_str = JSON.stringify(pack); + + // go through each target and send + // TODO: optimize so only one message is sent to each physical host target + for (var i=0; i < pack.targets.length; i++) + this.net.send(pack.targets[i], encode_str, is_reliable); +} + +// send a javascript object to a SINGLE target node (given message 'type' and 'priority') +msg_handler.prototype.sendMessage = function (target, msg_type, js_obj, priority, is_reliable) { + + // create a delivery package to send + var pack = new VAST.pack( + msg_type, + js_obj, + priority); + + pack.targets.push(target); + + //console.log('this.net: ' + this.net + ' target: ' + target); + + // convert pack to JSON or binary string + // NOTE: we pass the same context + return msg_handler.prototype.sendPack.call(this, pack, is_reliable); +} + +if (typeof module !== 'undefined') + module.exports = msg_handler; diff --git a/net_nodejs.js b/net/net_nodejs.js similarity index 98% rename from net_nodejs.js rename to net/net_nodejs.js index c17043db..41790567 100644 --- a/net_nodejs.js +++ b/net/net_nodejs.js @@ -74,7 +74,7 @@ 2012-09-24 add is_connected() */ -require('./common.js'); +require('../common.js'); var l_net = require('net'); // allow using network function net_nodejs(onReceive, onConnect, onDisconnect, onError) { @@ -180,7 +180,7 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // listen to a given port, while processing incoming messages at a callback this.listen = function (port, onDone) { - LOG.debug('listening at: ' + port); + LOG.debug('net_nodejs, listening: ' + port); try { _server = l_net.createServer(setupSocket); @@ -196,7 +196,7 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { }); _server.listen(port, function () { - LOG.debug('server started at port: ' + port); + LOG.debug('net_nodejs. server started at port: ' + port); if (typeof onDone === 'function') onDone(port); diff --git a/test_vast_net.js b/net/test_vast_net.js similarity index 83% rename from test_vast_net.js rename to net/test_vast_net.js index 0e6c005d..43dedde0 100644 --- a/test_vast_net.js +++ b/net/test_vast_net.js @@ -8,6 +8,7 @@ var vast_net = require('./vast_net'); + var net = undefined; // set default IP/port @@ -16,12 +17,16 @@ var ip_port = {host: "127.0.0.1", port: 37}; // get input IP / port // check port input, if any var port = process.argv[2]; -if (port !== undefined) +if (port !== undefined) { + console.log('custom port: ' + port); ip_port.port = port; +} var host = process.argv[3]; -if (host !== undefined) +if (host !== undefined) { + console.log('custom host: ' + host); ip_port.host = host; +} // check whether to run server or not // server @@ -46,10 +51,14 @@ if (process.argv.length <= 3) { } ); - net.listen(ip_port.port); + net.listen(ip_port.port, function (port) { + console.log('listen success, port binded: ' + port); + }); } // client else { + + console.log('client host: ' + ip_port.host + ' port: ' + ip_port.port); net = new vast_net( function (id, msg) { diff --git a/vast_net.js b/net/vast_net.js similarity index 96% rename from vast_net.js rename to net/vast_net.js index 6c10ebc7..c9a1e206 100644 --- a/vast_net.js +++ b/net/vast_net.js @@ -37,7 +37,7 @@ storeMapping(id, addr); store mapping from id to a particular host IP/port setID(new_id, old_id); set self ID or change the id -> socket mapping for incoming connections getID(); get self ID (which may be assigned by remote host) - send(id, msg, is_reliable); send a message to an target 'id' + send(id, msg, is_reliable); send a message to a target 'id' listen(port, CB_done); start a server at a given 'port', port binded is returned via 'CB_done', 0 indicates error disconnect(id) disconnect connection to a remote node @@ -62,8 +62,9 @@ 2012-07-20 rename switchID -> setID (can set self ID) */ -require('./common.js'); +//require('../common.js'); var l_net = require('./net_nodejs'); // implementation-specific network layer +//var VAST_ID_UNASSIGNED = 0; // // input: @@ -84,14 +85,16 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // return the host IP for the current machine this.getHost = function (CB_done) { + var hostname = require('os').hostname(); + LOG.debug('getHost called, hostname: ' + hostname); + // if already available, return directly if (_localIP !== undefined) return CB_done(_localIP); - require('dns').lookup(require('os').hostname(), function (err, addr, fam) { - - LOG.debug('hostname: ' + require('os').hostname()); - + + require('dns').lookup(hostname, function (err, addr, fam) { + if (err) { LOG.warn(err + '. Assign 127.0.0.1 to host'); _localIP = "127.0.0.1"; @@ -296,7 +299,8 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { if (port === undefined || port < 0 || port >= 65536) { LOG.error('port not provided, cannot start listening'); - CB_done(0); + if (typeof CB_done === 'function') + CB_done(0); return; } @@ -344,7 +348,8 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { if (port_binded != 0) { //LOG.debug('port bind successful: ' + port_binded); // return the actual port binded - CB_done(port_binded); + if (typeof CB_done === 'function') + CB_done(port_binded); return; } @@ -403,7 +408,7 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { var _processData = function (socket, data) { - //console.log('processData: ' + data); + console.log('vast_net id: ' + _self_id + ' processData: ' + data); // create buffer for partially received message, if not exist if (typeof socket.recv_buf === 'undefined') { diff --git a/test_VON_peer.js b/test_VON_peer.js index e93b80aa..1d47540d 100644 --- a/test_VON_peer.js +++ b/test_VON_peer.js @@ -15,8 +15,11 @@ var AUTOMATIC_LEAVE_PERIOD = 3; // number of seconds require('./common'); +//var curr = new Date(); +//LOG.debug(curr.toLocaleString()); + // do not show debug -LOG.setLevel(2); +LOG.setLevel(3); // set default IP/port var ip_port = {host: "127.0.0.1", port: 37700}; @@ -68,7 +71,6 @@ peer.join(ip_port, aoi, // try to move around once in a while... (if not gateway) if (id !== VAST_ID_GATEWAY) { interval_id = setInterval(function(){ moveAround() }, 1000); - } - + } } ); diff --git a/test_inhert.js b/test_inhert.js new file mode 100644 index 00000000..7e1fbe0d --- /dev/null +++ b/test_inhert.js @@ -0,0 +1,55 @@ + +/* inhertance test + +*/ + +function handler() { + this.public_str0 = 'hi world'; + var private_str = 'private world'; + + // calling this can use both public & private variables (both are accessible) + this.show_str = function () { + console.log('handler show_str: pubstr: ' + this.public_str + ' pristr: ' + private_str); + } +} + +handler.prototype.public_str = 'hello world'; + +var callback = function (onSuccess) { + + //this.public_str = 'def'; + console.log('callback called...'); + onSuccess(); +} + +handler.prototype.show2 = function () { + + var func = function () { + console.log('handler show2: pubstr: ' + handler.prototype.public_str); + } + + console.log('show2, public_str0: ' + this.public_str0); + callback(func); +} + +var von_handler = function () { + + this.von_str = 'VON message'; + var von_pri_str = 'private VON msg'; + + // print out + this.show = function () { + console.log('von_handler: pubstr: ' + this.public_str); + console.log('von_handler: von_pubstr: ' + this.von_str + ' pristr: ' + von_pri_str); + + this.show_str(); + } +} +von_handler.prototype = new handler(); + + +var vonnode = new von_handler(); +vonnode.show(); +console.log('external access of pub str: ' + vonnode.public_str); +vonnode.show_str(); +vonnode.show2(); diff --git a/vast.io/vast.io-server.js b/vast.io/vast.io-server.js index cadac370..a06ab5aa 100644 --- a/vast.io/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -7,7 +7,7 @@ process.on('uncaughtException', function(err) { // include VAST require('../common'); -LOG.setLevel(2); +LOG.setLevel(3); /* NOTE: From e31ce4e5abea496d1eb7af0a4a6613ab6cceb1e6 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 25 Oct 2012 08:30:21 +0800 Subject: [PATCH 008/141] set default error level to 2 (in common.js) --- VSS/VSS.js | 1 - common.js | 3 +++ test_VON_peer.js | 2 +- vast.io/vast.io-server.js | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/VSS/VSS.js b/VSS/VSS.js index b908130b..510efffe 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -1,7 +1,6 @@ // include VAST require('../common'); -LOG.setLevel(3); var server = require("./server"); var router = require("./router"); diff --git a/common.js b/common.js index aef42bf4..ea2098b9 100644 --- a/common.js +++ b/common.js @@ -11,6 +11,9 @@ var logger = require('./common/logger'); global.LOG = new logger(); global.UTIL = require('./common/util'); +// set default error level +LOG.setLevel(2); + // // VAST & VON // diff --git a/test_VON_peer.js b/test_VON_peer.js index 1d47540d..7cb550a8 100644 --- a/test_VON_peer.js +++ b/test_VON_peer.js @@ -19,7 +19,7 @@ require('./common'); //LOG.debug(curr.toLocaleString()); // do not show debug -LOG.setLevel(3); +//LOG.setLevel(2); // set default IP/port var ip_port = {host: "127.0.0.1", port: 37700}; diff --git a/vast.io/vast.io-server.js b/vast.io/vast.io-server.js index a06ab5aa..6b6e4361 100644 --- a/vast.io/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -7,7 +7,8 @@ process.on('uncaughtException', function(err) { // include VAST require('../common'); -LOG.setLevel(3); + +//LOG.setLevel(2); /* NOTE: From 3fce3574afc1a53260f267267e63a1d36f9f3f54 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 25 Oct 2012 08:34:47 +0800 Subject: [PATCH 009/141] turn off processData log --- VON_peer.js | 2 +- net/vast_net.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VON_peer.js b/VON_peer.js index 3d663183..d5529ea1 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -1267,7 +1267,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { that.sendPack.apply(that, arguments); } - LOG.warn('_net: ' + _net + ' this.net: ' + this.net); + //LOG.warn('_net: ' + _net + ' this.net: ' + this.net); // set default AOI buffer size if (l_aoi_buffer === undefined) diff --git a/net/vast_net.js b/net/vast_net.js index c9a1e206..1c0f6d3e 100644 --- a/net/vast_net.js +++ b/net/vast_net.js @@ -408,7 +408,7 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { var _processData = function (socket, data) { - console.log('vast_net id: ' + _self_id + ' processData: ' + data); + //console.log('vast_net id: ' + _self_id + ' processData: ' + data); // create buffer for partially received message, if not exist if (typeof socket.recv_buf === 'undefined') { From a9ea501210ae4de301b37366b5be29c9d22fd3ca Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 25 Oct 2012 08:48:55 +0800 Subject: [PATCH 010/141] no subscriber return empty array --- VSS/handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSS/handler.js b/VSS/handler.js index 4fab4b16..ac6542dd 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -163,7 +163,7 @@ var _replySubscribers = function (request, res) { } // return success - var error = (list.length === 0 ? ["no subscribers"] : []); + var error = []; _reply(res, [list, error]); } } From 16b759f16e8526639699b24f653300ed1d29efbd Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Fri, 26 Oct 2012 13:25:16 +0800 Subject: [PATCH 011/141] modify VON join behavior to discover neighbors immediately after VON_NODE is received --- VAST WorkLog.txt | 12 ++++++++- VON_peer.js | 63 +++++++++++++++++++++++++++++----------------- VSS/handler.js | 4 +-- net/msg_handler.js | 5 ++-- test_VON_scale.js | 1 + 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 05b5182a..ef186cac 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -52,6 +52,14 @@ BUG (*) denotes design bug: Overall Goals: extract non-VON-specific code from VON_peer as common code for handlers +make multiple handlers work with same network layer + + +2012-10-26 (5) +-------------- +goal: make subscriber list available upon first registerNode + + @@ -61,7 +69,9 @@ goal: extract non-VON-specific code from VON_peer as common code for msg_handler make multiple handlers work with same network layer modify VSS response - +- fixed response & deploy +- found a small issue: after initial publishPos, (registerNode), subscriber list is not + obtained (only valid after 2nd publishPos call). diff --git a/VON_peer.js b/VON_peer.js index d5529ea1..c7156115 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -63,6 +63,7 @@ require('./common.js'); // to be inherited by VON.peer var msg_handler = msg_handler || require('./net/msg_handler.js'); +// voronoi computation var Voronoi = require('./vast_voro.js'); // config @@ -474,15 +475,15 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var _updateNode = function (node) { - if (_isNeighbor (node.id) === false) + if (_isNeighbor(node.id) === false) return false; - // only update the node if it's at a later time + // only update the node if it's the same or a later time if (node.time < _neighbors[node.id].time) return false; - _voro.update (node.id, node.aoi.center); - _neighbors[node.id].update (node); + _voro.update(node.id, node.aoi.center); + _neighbors[node.id].update(node); // NOTE: should not reset drop counter here, as it might make irrelevant neighbor // difficult to get disconnected @@ -587,6 +588,9 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { //LOG.debug('voro nodes after insertion: ' + _voro.size()); + // record a list of relevant neighbors to be inserted + var relevant_neighbors = []; + // check through each newly inserted Voronoi for relevance for (var i=0; i < new_list.length; ++i) { @@ -601,8 +605,13 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // store new node as a potential neighbor, pending confirmation from the new node // this is to ensure that a newly discovered neighbor is indeed relevant - _potential_neighbors[target] = node; - + //_potential_neighbors[target] = node; + + // add a new relevant neighbor + // NOTE: we record first without inserting because we need to remove + // the new neighbor positions from Voronoi first + relevant_neighbors.push(node); + // notify mapping for sending Hello message // TODO: a cleaner way (less notifymapping call?) _net.storeMapping(node.id, node.endpt.addr); @@ -623,6 +632,10 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // clear up the temporarily inserted test node from Voronoi (if not becoming neighbor) for (var i=0; i < new_list.length; ++i) _voro.remove(new_list[i]); + + // insert new neighbors + for (var i=0; i < relevant_neighbors.length; i++) + _insertNode(relevant_neighbors[i]); // NOTE: erase new neighbors seems to bring better consistency // (rather than keep to next round, as in previous version) @@ -979,7 +992,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // if VON_NODE is received, we're considered joined // NOTE: do this first as we need to update our self ID for later VON_NODE process to work - LOG.warn('checking joining state: ' + _state); + //LOG.warn('checking joining state: ' + _state); if (_state === NodeState.JOINING) { // check if we're getting new ID @@ -992,8 +1005,9 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _deleteNode(_self.id); _self.id = selfID; _insertNode(_self); - - _setJoined(); + + // NOTE: we don't notify join success until neighbor list is processed + // so that upon join success, the client already has a list of neighbors } var nodelist = pack.msg; @@ -1028,10 +1042,12 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { } } // end going through node list - // process new neighbors - // NOTE: do this collectively during tick - //_contactNewNeighbors(); - + // process new neighbors if we just joined, + // otherwise, do this periodically & collectively during tick + if (_state === NodeState.JOINING) { + _contactNewNeighbors(); + _setJoined(); + } } break; @@ -1061,18 +1077,19 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // VON's hello response case VON_Message.VON_HELLO_R: { - // check if it's a response from new neighbor - if (_potential_neighbors.hasOwnProperty(from_id) === true) { + // check if it's a response from an existing neighbor + if (_neighbors.hasOwnProperty(from_id) === true) { - // insert the new node as a confirmed neighbor with updated position - var neighbor = _potential_neighbors[from_id]; + // update the neighbor's position + // NOTE: that 'time' is not updated here + var neighbor = _neighbors[from_id]; neighbor.aoi.center.parse(pack.msg); LOG.debug('got latest pos: ' + neighbor.aoi.center.toString() + ' id: ' + from_id); - _insertNode(neighbor); - - delete _potential_neighbors[from_id]; - } + _updateNode(neighbor); + } + else + LOG.warn('got VON_HELLO_R from unknown neighbor [' + from_id + ']'); } break; @@ -1213,7 +1230,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _neighbor_states = {}; _updateStatus = {}; - _potential_neighbors = {}; + //_potential_neighbors = {}; _req_nodes = {}; _time_drop = {}; @@ -1304,7 +1321,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // TODO: combine these structures into one? var _new_neighbors; // nodes worth considering to connect (learned via VON_NODE messages) var _neighbor_states; // neighbors' knowledge of my neighbors (for neighbor discovery check) - var _potential_neighbors; // neighbors to be discovered + //var _potential_neighbors; // neighbors to be discovered var _req_nodes; // nodes requesting neighbor discovery check var _updateStatus; // status about whether a neighbor is: 1: inserted, 2: deleted, 3: updated diff --git a/VSS/handler.js b/VSS/handler.js index ac6542dd..d1406b0d 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -149,9 +149,9 @@ var _replySubscribers = function (request, res) { // NOTE: current approach can only do ident translation for nodes created via this VSS server LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); - // do not return if: + // do not return a node if: // 1. is self - // 2. no mapping for ident + // 2. no mapping for ident (the node is not created via VSS) // 3. is not a subscriber to myself (subscribed area does not cover me) if (self.id == id || _id2ident.hasOwnProperty(id) === false || diff --git a/net/msg_handler.js b/net/msg_handler.js index 1b7a8fe6..923dc135 100644 --- a/net/msg_handler.js +++ b/net/msg_handler.js @@ -65,7 +65,7 @@ function msg_handler(l_connHandler, l_disconnHandler, l_packetHandler, l_self_id // // constructor - LOG.debug('msg_handler init, l_packetHandler: ' + typeof l_packetHandler); + //LOG.debug('msg_handler init, l_packetHandler: ' + typeof l_packetHandler); // NOTE: packet & message handlers cannot be of prototype-style, // as they need to occupy memory independently for each msg handler instance @@ -75,7 +75,7 @@ function msg_handler(l_connHandler, l_disconnHandler, l_packetHandler, l_self_id // keep local reference for 'this' var _that = this; - + // handler for incoming messages this.msgHandler = function (from_id, msg) { @@ -136,7 +136,6 @@ function msg_handler(l_connHandler, l_disconnHandler, l_packetHandler, l_self_id // create network layer & start listening // NOTE: internal handlers must be defined before creating the VAST.net instance this.net = new VAST.net(this.msgHandler, l_connHandler, l_disconnHandler, l_self_id); - } // end msg_handler diff --git a/test_VON_scale.js b/test_VON_scale.js index 9f29a976..8f39e930 100644 --- a/test_VON_scale.js +++ b/test_VON_scale.js @@ -26,6 +26,7 @@ var node_radius = 200; // set default IP/port var gateway_addr = {host: "127.0.0.1", port: 37700}; +// IP/port if (process.argv[2] !== undefined) { var ip_port = process.argv[2]; // check if this is port only From d7e2ff81893a9a46b9e19fb939280368131ea060 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Sat, 27 Oct 2012 09:34:50 +0800 Subject: [PATCH 012/141] fix VSS dealing with invalid request --- VAST WorkLog.txt | 15 +++++++++++++-- VSS/handler.js | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index ef186cac..617235f4 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -55,13 +55,24 @@ extract non-VON-specific code from VON_peer as common code for handlers make multiple handlers work with same network layer -2012-10-26 (5) +2012-10-27 (6) -------------- -goal: make subscriber list available upon first registerNode +goal: test whether VSS server can scale on different machines + +2012-10-26 (5) +-------------- +goal: make subscriber list available upon first registerNode + +CHANGE: modify join behavior in VON: neighbor list is build immediately after VON_NODE is received. + not after VON_HELLO_R is received. This makes it quicker to discover initial list of neighbors + (save one less round-trip). + +- subscribe list available upon join finished & tested. + 2012-10-25 (4) -------------- diff --git a/VSS/handler.js b/VSS/handler.js index d1406b0d..a4f839ab 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -135,7 +135,7 @@ var _replySubscribers = function (request, res) { var node = _getNode(request, res); - if (typeof node === 'object') { + if (node !== undefined && node !== null) { // get a list of current neighbors's id var neighbors = node.list(); @@ -324,7 +324,7 @@ function subscribe(words, res) { // check if node exists, return error if not yet exist (need to publishPos first) var node = _getNode(request, res); - if (typeof node === 'object') { + if (node != undefined && node !== null) { // update AOI radius for area subscription _publishPos(node, node.getSelf().aoi.center, request.radius); From 6509d2221f882c13157d035cac2ac49fe227c6b5 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 30 Oct 2012 14:42:16 +0800 Subject: [PATCH 013/141] VSS server to perform id to ident mapping --- VAST WorkLog.txt | 27 ++++++++++++++++- VON_peer.js | 78 ++++++++++++++++++++++++++++++++++++++++-------- VSS/handler.js | 29 +++++++++++++++--- common.js | 2 +- vast_types.js | 10 ++++++- 5 files changed, 126 insertions(+), 20 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 617235f4..359455c3 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -54,13 +54,38 @@ Overall Goals: extract non-VON-specific code from VON_peer as common code for handlers make multiple handlers work with same network layer +2012-10-30 (2) +-------------- +goal: VSS server to support ident exchange + + + + +2012-10-29 (1) +-------------- +goal: VSS server to support ident exchange + +- fix 'meta' field exchange in VON peer (now storing & sending via HELLO & HELLO_R works correctly) + + +2012-10-28 (7) +-------------- +goal: add put() & get() functions to VON Peer + +- implemented & tested storing & sending meta-data via VON_HELLO/VON_HELLO_R. +- still to do: storing & retriving node ident as meta-data at VSS servers + 2012-10-27 (6) -------------- goal: test whether VSS server can scale on different machines +- setup VSS servers on both dev & prod servers, found that node discovery works correctly ^^ + however, node ident is not shared across different VSS servers, so + subscriber list did not return correctly (need to find a solution for this...) - +- try to transfer node-specific info along with HELLO / HELLO_R messages + add put() and get() to VON_peer's interface... 2012-10-26 (5) diff --git a/VON_peer.js b/VON_peer.js index c7156115..871ce333 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -43,8 +43,10 @@ join(addr, aoi, done_CB) join a VON network with a given gateway (entry) leave() leave the VON network move(aoi, send_time) move the AOI to a new position (or change radius) - list(); get a list of AOI neighbors - send(id, msg); send a message to a given node + list() get a list of AOI neighbors + send(id, msg) send a message to a given node + put(obj) store a app-specific data along with the node (will pass during node discovery) + get() retrieve app-specific data for this node // accessors isJoined(); check if we've joined the VON network @@ -152,7 +154,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // check if already joined if (_state === NodeState.JOINED) { LOG.warn('VON_peer.join(): node already joined'); - if (done_CB !== undefined) + if (typeof done_CB === 'function') done_CB(_self.id); return; } @@ -333,6 +335,18 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var _send = this.send = function (id, msg) { } + + // store a app-specific data along with the node (will pass during node discovery) + var _put = this.put = function (obj) { + _meta = obj; + LOG.debug('after put() meta keys: ' + Object.keys(_meta).length); + } + + // retrieve app-specific data for this node + var _get = this.get = function () { + LOG.debug('get() meta keys: ' + Object.keys(_meta).length); + return _meta; + } ///////////////////// // public accessors @@ -536,7 +550,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _state = NodeState.JOINED; // notify callback - if (_join_done_CB !== undefined) + if (typeof _join_done_CB === 'function') _join_done_CB(_self.id); // start ticking @@ -848,11 +862,21 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // list: a list of id to be sent var _sendHello = function (target) { - LOG.debug('sendHello target: ' + target); - + LOG.debug('sendHello target: ' + target); + + var node = _getNode(_self.id); + + LOG.debug('meta keys: ' + Object.keys(_meta).length); + + // add app-specific attributes, if exist + if (Object.keys(_meta).length > 0) { + LOG.warn('storing meta data to node to be sent for VON_HELLO'); + node.meta = _meta; + } + // prepare HELLO message // TODO: do not create new HELLO message every time? (local-side optimization) - _sendMessage(target, VON_Message.VON_HELLO, _getNode(_self.id), VON_Priority.HIGHEST, true); + _sendMessage(target, VON_Message.VON_HELLO, node, VON_Priority.HIGHEST, true); } // send a particular node its perceived enclosing neighbors @@ -1057,17 +1081,33 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var node = new VAST.node(); node.parse(pack.msg); LOG.debug('node: ' + node.toString()); - + + // store meta data, if any + if (pack.msg.hasOwnProperty('meta') && typeof pack.msg.meta === 'object') { + LOG.warn('VON_HELLO storing to node meta keys: ' + Object.keys(pack.msg.meta).length); + node.meta = pack.msg.meta; + } + // update existing or new neighbor status if (_isNeighbor (from_id)) _updateNode(node); else _insertNode(node); - + // send HELLO_R as response (my own position, reliably) - var pos = new VAST.pos(); + // NOTE: we need to create a new pos as self.aoi.center has other properties (put by Voronoi) + // TODO: this should be clean-up + var pos = new VAST.pos; pos.parse(_self.aoi.center); - _sendMessage(from_id, VON_Message.VON_HELLO_R, pos, VON_Priority.HIGH, true); + var res_obj = { + pos: pos + }; + + // add meta-data, if any + if (Object.keys(_meta).length > 0) + res_obj.meta = _meta; + + _sendMessage(from_id, VON_Message.VON_HELLO_R, res_obj, VON_Priority.HIGH, true); // check if enclosing neighbors need any update _checkConsistency(from_id); @@ -1083,10 +1123,16 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // update the neighbor's position // NOTE: that 'time' is not updated here var neighbor = _neighbors[from_id]; - neighbor.aoi.center.parse(pack.msg); + neighbor.aoi.center.parse(pack.msg.pos); + + // store meta data, if any + if (pack.msg.hasOwnProperty('meta') && typeof pack.msg.meta === 'object') { + LOG.warn('meta keys received: ' + Object.keys(pack.msg.meta).length); + neighbor.meta = pack.msg.meta; + } LOG.debug('got latest pos: ' + neighbor.aoi.center.toString() + ' id: ' + from_id); - _updateNode(neighbor); + _updateNode(neighbor); } else LOG.warn('got VON_HELLO_R from unknown neighbor [' + from_id + ']'); @@ -1240,6 +1286,9 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // init stat collection for selected messsage types _stat = {}; _stat[VON_Message.VON_NODE] = new VAST.ratio(); + + // clear meta data + _meta = {}; } var _connHandler = function (id) { @@ -1334,6 +1383,9 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // init stat collection for selected messsage types var _stat; + // app-specific meta data + var _meta; + // clean all states _initStates(); diff --git a/VSS/handler.js b/VSS/handler.js index a4f839ab..71a6407e 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -8,6 +8,8 @@ require('../common'); // default port for connecting / creating VON nodes var _VON_port = 37700; +var _VON_gateway = '127.0.0.1'; +//var _VON_gateway = 'dev.imonology.com'; // default radius when first joined // TODO: need to have default radius? @@ -46,12 +48,21 @@ var _registerNode = function (pos, info, done_CB) { var aoi = new VAST.area(pos, _default_radius); // specify where to locate VON gateway - var ip_port = new VAST.addr('127.0.0.1', _VON_port); + var ip_port = new VAST.addr(_VON_gateway, _VON_port); var new_node = new VON.peer(VAST_ID_UNASSIGNED, ip_port.port + _nodes_created); _nodes_created++; LOG.debug('new_node (before join): ' + new_node.getSelf().toString()); - + + // store node ident for ident discovery across different VSS servers + var ident_info = { + apikey: info.apikey, + layer: info.layer, + ident: info.ident + }; + + new_node.put(ident_info); + // join in the network new_node.join(ip_port, aoi, @@ -71,7 +82,9 @@ var _registerNode = function (pos, info, done_CB) { // store id to ident mapping LOG.debug('store mapping for node id: ' + self_id + ' ident: ' + info.ident); _id2ident[self_id] = info; - + + //new_node.put(info); + // check content LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); @@ -142,12 +155,20 @@ var _replySubscribers = function (request, res) { var list = []; var self = node.getSelf(); - + // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) for (var id in neighbors) { // convert node id to node ident (only for those registered here) // NOTE: current approach can only do ident translation for nodes created via this VSS server + var neighbor = neighbors[id]; + if (neighbor.hasOwnProperty('meta')) { + LOG.warn('neighbor [' + id + '] has meta!'); + var info = neighbor.meta; + for (var i in info) + LOG.debug(i + ': ' + info[i]); + } + LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); // do not return a node if: // 1. is self diff --git a/common.js b/common.js index ea2098b9..4ef9027b 100644 --- a/common.js +++ b/common.js @@ -12,7 +12,7 @@ global.LOG = new logger(); global.UTIL = require('./common/util'); // set default error level -LOG.setLevel(2); +LOG.setLevel(3); // // VAST & VON diff --git a/vast_types.js b/vast_types.js index 4559e6e9..2c3cd6ea 100644 --- a/vast_types.js +++ b/vast_types.js @@ -234,6 +234,7 @@ var l_node = exports.node = function (id, endpt, aoi, time) { this.endpt = endpt; // a node's contact endpoint (host_id & address) this.aoi = aoi; // a node's AOI (center + radius) this.time = time; // a node's last updated time (used to determine whether it contains newer info) + //this.meta = {}; // a node's meta-data, default to empty // update node info from another node // NOTE: this is powerful as it replaces almost everything (perhaps ID shouldn't be replaced?) @@ -242,6 +243,9 @@ var l_node = exports.node = function (id, endpt, aoi, time) { if (new_info.id !== 0) this.id = new_info.id; + + //if (typeof new_info.meta === 'object' && Object.keys(new_info.meta).length > 0) + // this.meta = new_info.meta; this.endpt.update(new_info.endpt); this.aoi.update(new_info.aoi); @@ -262,6 +266,9 @@ var l_node = exports.node = function (id, endpt, aoi, time) { this.aoi = new l_area(); this.aoi.parse(js_obj.aoi); this.time = js_obj.time; + + //if (typeof js_obj.meta === 'object') + // this.meta = js_obj.meta; } catch (e) { console.log('node parse error: ' + e); @@ -270,7 +277,8 @@ var l_node = exports.node = function (id, endpt, aoi, time) { // print out node info this.toString = function () { - return '[' + this.id + '] ' + this.endpt.toString() + ' ' + this.aoi.toString(); + //return '[' + this.id + '] ' + this.endpt.toString() + ' ' + this.aoi.toString() + ' meta: ' + Object.keys(this.meta).length; + return '[' + this.id + '] ' + this.endpt.toString() + ' ' + this.aoi.toString(); } } From 01cc6017b51ece1a312742e058cbf430b465ae4a Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 30 Oct 2012 16:58:52 +0800 Subject: [PATCH 014/141] bugfix: meta data discovery between remote VSS servers --- VON_peer.js | 37 ++++++++++++++++++++++++++----------- VSS/handler.js | 31 +++++++++++++++++++------------ 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/VON_peer.js b/VON_peer.js index 871ce333..a669b771 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -339,12 +339,15 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // store a app-specific data along with the node (will pass during node discovery) var _put = this.put = function (obj) { _meta = obj; - LOG.debug('after put() meta keys: ' + Object.keys(_meta).length); + LOG.warn('after put() meta keys: ' + Object.keys(_meta).length); + + // update to self (so that it can be sent over) + _self.meta = _meta; } // retrieve app-specific data for this node var _get = this.get = function () { - LOG.debug('get() meta keys: ' + Object.keys(_meta).length); + LOG.warn('get() meta keys: ' + Object.keys(_meta).length); return _meta; } @@ -430,21 +433,21 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var _insertNode = function (node) { - //LOG.debug('insertNode [' + node.id + '] isNeighbor: ' + _isNeighbor(node.id)); + LOG.debug('insertNode [' + node.id + '] isNeighbor: ' + _isNeighbor(node.id)); // check for redundency if (_isNeighbor(node.id)) return false; // notify network layer about connection info, no need to actually connect for now - _net.storeMapping (node.id, node.endpt.addr); + _net.storeMapping(node.id, node.endpt.addr); // update last access time node.endpt.lastAccessed = UTIL.getTimestamp(); //LOG.debug('last accessed: ' + node.endpt.lastAccessed); // store the new node to - _voro.insert (node.id, node.aoi.center); + _voro.insert(node.id, node.aoi.center); // store to neighbors _neighbors[node.id] = node; @@ -459,7 +462,10 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // update node status _updateStatus[node.id] = NeighborUpdateStatus.INSERTED; - LOG.debug('[' + _self.id + '] insertNode neighbor (after insert) size: ' + Object.keys(_neighbors).length + ' voro: ' + _voro.size()); + var meta_keys = node.hasOwnProperty('meta') ? Object.keys(node.meta).length : 0; + LOG.warn('[' + _self.id + '] insertNode neighbor (after insert) size: ' + Object.keys(_neighbors).length + + ' voro: ' + _voro.size() + + ' meta: ' + meta_keys); return true; } @@ -498,6 +504,10 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _voro.update(node.id, node.aoi.center); _neighbors[node.id].update(node); + + // update meta, if available + if (node.hasOwnProperty('meta')) + _neighbors[node.id].meta = node.meta; // NOTE: should not reset drop counter here, as it might make irrelevant neighbor // difficult to get disconnected @@ -514,8 +524,8 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { return true; } - // get a clean node to send (no extra stuff in 'node.aoi.center') - // TODO: remove this? + // get a clean node to send (no extra stuff in 'node.aoi.center', for example) + // TODO: remove this? (will need to make sure all nodes are clean) // do not keep anything else besides the original attributes var _getNode = function (id) { var node = new VAST.node(); @@ -868,9 +878,10 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { LOG.debug('meta keys: ' + Object.keys(_meta).length); - // add app-specific attributes, if exist + // add app-specific attributes, if exist + // TODO: find a cleaner way (for example, meta already exists in the node returned by getNode) if (Object.keys(_meta).length > 0) { - LOG.warn('storing meta data to node to be sent for VON_HELLO'); + LOG.warn('storing meta keys ' + Object.keys(_meta).length + ' to node to be sent for VON_HELLO'); node.meta = _meta; } @@ -1076,6 +1087,10 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { break; // VON's hello, to let a newly learned node to be mutually aware + // NOTE: it's found a node may already knows a neighbor even if it receives + // VON_HELLO for the first time + // this occurs when the current node first receives VON_QUERY and had inserted + // the joining node before case VON_Message.VON_HELLO: { var node = new VAST.node(); @@ -1089,7 +1104,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { } // update existing or new neighbor status - if (_isNeighbor (from_id)) + if (_isNeighbor(from_id)) _updateNode(node); else _insertNode(node); diff --git a/VSS/handler.js b/VSS/handler.js index 71a6407e..4b555e69 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -8,8 +8,8 @@ require('../common'); // default port for connecting / creating VON nodes var _VON_port = 37700; -var _VON_gateway = '127.0.0.1'; -//var _VON_gateway = 'dev.imonology.com'; +//var _VON_gateway = '127.0.0.1'; +var _VON_gateway = 'dev.imonology.com'; // default radius when first joined // TODO: need to have default radius? @@ -148,6 +148,8 @@ var _replySubscribers = function (request, res) { var node = _getNode(request, res); + LOG.debug('replySubscribers called, node id: ' + node.getSelf().id); + if (node !== undefined && node !== null) { // get a list of current neighbors's id @@ -158,28 +160,33 @@ var _replySubscribers = function (request, res) { // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) for (var id in neighbors) { + + LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); + // convert node id to node ident (only for those registered here) // NOTE: current approach can only do ident translation for nodes created via this VSS server var neighbor = neighbors[id]; - if (neighbor.hasOwnProperty('meta')) { - LOG.warn('neighbor [' + id + '] has meta!'); - var info = neighbor.meta; - for (var i in info) - LOG.debug(i + ': ' + info[i]); - } - LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); + for (var i in neighbor) + LOG.debug(i + ': ' + typeof neighbor[i]); + + // get node ident (from either 'meta' field or from mapping) + var info = undefined; + if (neighbor.hasOwnProperty('meta')) + info = neighbor.meta; + else if (_id2ident.hasOwnProperty(id)) + info = _id2ident[id]; + // do not return a node if: // 1. is self // 2. no mapping for ident (the node is not created via VSS) // 3. is not a subscriber to myself (subscribed area does not cover me) if (self.id == id || - _id2ident.hasOwnProperty(id) === false || + info === undefined || _isSubscriber(neighbors[id], self.aoi) === false) continue; - var info = _id2ident[id]; list.push(info.apikey + ':' + info.layer + ':' + info.ident); } @@ -234,7 +241,7 @@ var _getNode = function (req, res) { var _isSubscriber = function (node, aoi) { var result = node.aoi.covers(aoi.center); - LOG.debug('check if node [' + node.toString() + '] covers ' + aoi.center.toString() + ': ' + result); + LOG.debug('isSubscriber check if node ' + node.toString() + ' covers ' + aoi.center.toString() + ': ' + result); return result; } From c43c64945c095fca427d584f439ea8c9c489bd53 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 22 Nov 2012 12:16:32 +0800 Subject: [PATCH 015/141] VON_PING added, msg_handler work with VON_peer --- .gitignore | 1 + VAST WorkLog.txt | 212 +++++++++++++- VAST.js | Bin 16990 -> 12600 bytes VAST.js architecture.txt | 29 +- VAST_matcher.js | 409 ++++++++++++++++++++++++++ VON_peer.js | 596 ++++++++++++++++++++++---------------- VSO_peer.js | 5 +- VSS/handler.js | 10 +- common.js | 48 ++- net/msg_handler.js | 292 +++++++++++++------ net/vast_net.js | 4 +- test_VAST_client.js | 81 ++++++ test_VON_peer.js | 63 ++-- test_VON_scale.js | 25 +- vast.io/vast.io-server.js | 31 +- vast_types.js | 90 ++++-- 16 files changed, 1452 insertions(+), 444 deletions(-) create mode 100644 .gitignore create mode 100644 VAST_matcher.js create mode 100644 test_VAST_client.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..91cf85d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vast.io/node_modules \ No newline at end of file diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 359455c3..e19150de 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -21,8 +21,6 @@ BUG (*) denotes design bug: 2012-08-29: scaling many nodes will cause TCP connection errors 2012-09-03: EADDRINUSE crash bug (after running a while with >50 nodes) 2012-09-24: send_msg in vast_net does not necessarily send out in FIFO order -2012-09-29a: EPERM crashing error when other nodes shutdown with Ctrl-C (unexpectedly) -2012-09-29b: VON topology not updated when VSS/vast.io server shutdowns ---- @@ -51,15 +49,219 @@ BUG (*) denotes design bug: - 2010-10-06 after repeated join/leave of worlds, cannot join again (gateway could not assign origin matcher) Overall Goals: -extract non-VON-specific code from VON_peer as common code for handlers -make multiple handlers work with same network layer +implement generic SPS based on matcher-client design +simplify msg_handler usage (handler needs to implement many things...) + + + +2012-11-22 (4) +------------- +goal: make VON_peer to work with VON_PING to obtain initial ID + +- add support of VON_PING and modify join procedure +- modify relevant programs that use VON_peer for new interface (vast.io-server, test_VON_peer, test_VON_scale) + +- seems working for up to 50 nodes via test_VON_scale. but there are many + "got VON_HELLO_R from unknown neighbor [xx]" warnings + also many remote id is NaN & negative ID are received... many are VON_HELLO, VON_EN, VON_MOVE & VON_MOVE_B + + + +2012-11-21 (3) +-------------- +goal: implement subscribe() in VAST.js (matcher-side) + +- modify vast.io-server & test_VON_scale to suit new VON_Peer usage + +BUG: only a few nodes can join successfully (out of 50) + > cause is that when VON_JOIN is received, it considers the packet coming from gateway, so + the acceptor replies to gateway only (and not the joining node) + solution will be to separate ID obtain from query/join (add a VON_PING message) + + +2012-11-20 (2) +-------------- +goal: debug query() in VON_Peer + +MILESTONE +- done debug, now can join up to 3 nodes using VON_QUERY to send join request + +2012-11-19 (1) +-------------- +goal: a flexible & efficient query() in VON_Peer + +- implemented query() in VON_Peer + +2012-11-18 (7) +-------------- +goal: a flexible & efficient query() in VON_Peer + +- figure out we need a 'forward' action to a certain destination + can use 'forward' to send 'VON_JOIN' and 'VAST_SUB' + + +2012-11-17 (6) +-------------- +goal: extract query() from join() in VON_Peer + +- split original join() into query() & join() in VON_Peer, + but discover that handling of VON_QUERY message actually currently returns initial + neighbor list, and therefore cannot be split to query response & join response easily. + without adding join latency (first got query, then send out another join request). + +2012-11-16 (5) +-------------- +goal: implement subscribe() in VAST.js (matcher-side) + +- found out may need to extract VON_QUERY in VON_peer into more general form, + so both VON and upper layers (such as VAST/VSO) can use it directly... + might need to change VON join procedure into: + - query for acceptor of the joining locatin + - join request to acceptor directly (either accept or refuse) +- possibly can use this for Relay search as well... + + +2012-11-15 (4) +-------------- +goal: implement subscribe() in VAST.js + +- done, but need to test + +2012-11-14 (3) +-------------- +goal: implement subscribe() in VAST.js + +- add sub datatype to vast_types for 'subscription' record + + +2012-11-13 (2) +-------------- +goal: implement join() in VAST.js + +- keep converting C++ join procedure to js + +DESIGN CHANGES (simplify things) +1. remove 'world_id' parameter when joining VAST + (keep VAST itself simpler, we can use other meta-mechanisms for multiple world support) +2. add 'query' method to VON_Peer, so a joining VAST client can find its initial owner matcher +3. remove 'join' 'leave' procedures in VAST (client), as querying can be done when subscribing +4. remove 'group' parameter from VAST.pack (packet format to transmit) + + +2012-11-12 (1) +-------------- +goal: implement join() in VAST.js + +- port join C++ code to js +- send world_id successfully.. + +2012-11-11 (7) +-------------- +goal: implement join() in VAST.js + +- test successfully sending an initial message from VAST client to gateway, and all handlers can process the message + + +2012-11-10 (6) +-------------- +goal: implement join() in VAST.js + +- make NodeState global as 'VAST.state' + make VON_Priority global as 'VAST.priority' + +- allows VON peers to store key/value pairs which can be migrated automatically? + (this will allow for load balancing, also a simpler design for VON peers that + supports VSO, as well as other things, e.g., VSS servers' 'ident' info exchange) + +- review main functions for VAST.js (VAST.js architecture file) + +2012-11-09 (5) +-------------- +goal: implement join() in VAST.js + +- figure out will need to have both VAST and VAST_matcher logic to implement join() + + +2012-11-08 (4) +-------------- +goal: make VAST.js and VON_Peer.js work with same network layer + revise how msg_handler processes net layer + +- finish revise msg_handler mechanism, can run correctly + +MILESTONE +- VAST.js & VON_Peer.js now use same network layer + +2012-11-07 (3) +-------------- +goal: make VAST.js and VON_Peer.js work with same network layer + revise how msg_handler processes net layer + +- put net object inside msg_handler and hide it + + +2012-11-06 (2) +-------------- +goal: make VAST.js and VON_Peer.js work with same network layer + +- try to use VON_peer within VAST.. found out need to revise how network binding is done + + +2012-11-05 (1) +-------------- +goal: make multiple handlers work with same network layer + +- finish implement a version, but calling sendMessage/sendPack has incorrect context... +- change from prototype usage to all internal / private methods (seems to work correctly) + + +2012-11-04 (7) +-------------- +goal: make multiple handlers work with same network layer + +- convert msg_handler to support addHandler/removeHandler functions +- pending converting VON_peer to use new msg_handler class + + +2012-11-03 (6) +-------------- +goal: integrate VAST.js to use msghandler with VONPeer + implement join() in VAST.js + +- try re-designing VONpeer as a pluggable handle to msg_handler + + +2012-11-02 (5) +-------------- +goal: integrate VAST.js to use msghandler with VONPeer + implement join() in VAST.js + +- checking what's required for a join procedure in VAST.js + +2012-11-01 (4) +-------------- +goal: make multiple handlers work with same network layer + add a VAST_Client handler + +- merge VON_BYE with VON_DISCONNECT into VON_BYE + +- review VAST.js interface definition (adopted from VAST_Client.h) + +2012-10-31 (3) +-------------- +goal: make multiple handlers work with same network layer +- study how to add another logic layer on top of current message handler 2012-10-30 (2) -------------- goal: VSS server to support ident exchange +MILESTONE: +- VSS server can now exchange meta data for the VON nodes created independently ^^ - +- known issue: a remote node's ident may not be discovered immediately after a node performs publishPos. + will wait till the next publishPos is performed. + 2012-10-29 (1) -------------- diff --git a/VAST.js b/VAST.js index 3c24b0f8ff1501e796547485821f3b49cffeae3b..42d274cb1a16289829c0f32388d395d61197cd50 100644 GIT binary patch literal 12600 zcmbtb`)}LG5&pRb{6B16^em^cZ0B$d>eRV9i61KB2Q1}+;Cc`!ab@vBkt!edxoQ6Q ze&6hilw>Du6`VzpyR-A!nQw-Bn;U!2HaFBW_0#cS*j14#Cs7)WQ>_+S7rRxy%WswG z>OL<%scBxQyQHYze@nF1y}Y>H;XIi;oF%17lW`Fh4+@vlLTgp#)9OAd^h@=S*J=`Fs?c## zRz)(dE3J}BMOnO;7eZm4$H^3PD^+JPSgvMTRl1m$Dxb=av&;ADoi0l}JJXpiAi{M$ zPLqjDf0s;jR%#WMYQekZOvh^cATv+N%)m0EPID{}RY{(`R64;jX8k)|l(?s!_YT~O zmS$H$Yn`Z~6otwcoVo+nA5D7%oR@cWj!~UE1?~ZTO_4}LatHDW6sR49F-9z0* zvFlW>oVw{y^Ssb1)>V|GWl+LLn7jm|X{=_^ordWrI=KUzk($8KoQ}WIs%f6xisaxe zc-l*qOjVXwU3Fh1@Te*m)?4-#A%e}js-I1IUG>9(I)%1Ar7*|<1WuD_HB+Z)o)=y9 zIxnj{>#B=mrS_j69PIBNe7}F7)ce6P$??Cv&5cd7_momY=xZF6$pq$wM@*xMmaP`S zuJhP3vqz=MgPoT3Ig^c~K=0tz5t(;nCK2@n~F5uurt=46X+|imPFUn*ud@ zVu$95`MbAg89^9G$I;}I+EdtY^`xo`ZAs{gOP1P zE_iDOeRP!!whsy&nfYZ`6;YhjrJXV_Z-vUV$mgRZ?y55Rm+q?SVS&Hcc2O7MVN~ce zVU+r!6gZy4#iCop3xp!|UGNla-w8b1#3vDcEVQ8vl-kS1-b zNZGWo&NMdyn7<_S=5?pgWt~Dm{OrHop_&^X(D1s1e_1znbYUPf85&CYb>STGWcMx04$`AzycO(c+ucJqrj}c|evdU)2$H)97 z4{%HI6A8z7LXU-JWG$%1I!#xdeHhU#tb~5PaewcW=Dt<_Sa+Ox;5VXY2w<7_W6X}p z6cR?H5EvtMi+GV4%NlxiceJT3>A~%w$VtkmfYkE@*?E4aVLlii4usX$sg+}xUX9Bc zcOqff_#Qi>Ot`VF)H;hhbp0jHsBiayt1}Wh&j+P)p(n|daU5x&=#*B$w0nn4b(`MQ z=I~ZiTO7%#>J?g)5-}0rtoXv7;gLI|f|B=CBwm|iuz3Pv*Ru!2vM9Z@?pt=+>#6pF z3p?C1SgJ`~6xg@vgZh-^_f{{M^)A1ClJFAc)C~U`;X*Ssogo?DM-Pwe3sHEP&oy{M zaGy6-e`{u{4zrn63p_tBV)aSu1%jnDhFRzeASuZnS%*O^2J%_EP%bl(5~>Mc$Jf_` z#s;v`vihA9$v15wJVPqLE0yL9R}bEp1D#I4N`Qe;0sNF0jD#B7RKfMREuNas_2&rG z#^TQV%m(SdMOZW#pwP%bnVKWeCW}=6Xf=x2AMzrNpIXDN000Xt(SPeDdk3>+6EQf` z%aV^mIh;s{hCxO}v~Q#q5pwRNP9s1Un|V0j&^3SU_F!UD;-Q*i)rKjeq*5pXZgoZVe@>=ljQ#1kzgSsRdP81_HL{**s^$wg99te%- z;?4?fw?2>+eCWXf3VIjt|MT7b7rXm^7GLv!k__<a}e?KQ>*A|@qHlUqZN*>LpsSk!!KT_YncH5bCqT zwN$r5tPla1p2eV-Bz8Nsgt(@TFx!f<0Zj@@fjDyI2#GULoggj&E*i6LVsYalzU%?k zfA|O~_u$Ck90h;bI(Yt~w~v1Zhu^<=vA@41b}_TeU#!3wtf@ZkdZ?>G)I2&eyFq^R zj$aQ>E{9DH5ksxGCU%+x)TC06At4>Hhk+gXI@$INP{abMg+*+kZ=)in(Og^(L0#lz zp!eQIB7qD_3xr8zZFrl_R6y?5r(qkbq6TMn6fn7EaMXW0Iy)Yod^rB-xs*Ny8O8_d z0vA#Nl!QA{sZ+8y0`~z%=ZieUrgwaC(jD9`FaCPT9rWxK?PVnQa ze!vfgY0g|r%^{gpYVOH5ld{moQli9;u@z7gm0g-9sFj*_!t^Sc zYiu=JV{j$jf#=T`4Y6Rn->T6<7YSNF6K_D?4dclIhS0TD6_ua9M$;}m@}c7;b#L#A zqC&iE2TQAvpsRVb_$%#z>NRlxN{!M8HmDaUX3^Qg2lRPMnOid0)G|$^;T*N8sjw7C zF!izsrY(~UJAk{_6>F9^yV?26`tOAWFtQ;=$Yk^~as`Sef~+SPmZp7Q*V8Gi?LQ`> zj%$Vp5Q<2O%^QVV9JrRpK+-ei31X*GMc~i!CJM9CIqC>J#0;)iNq0ke9qUwAI&NTy z2GzBf;COiT_UaIPk5RUQUz7!g=jun#rN5wr49X49qUFIWQ6bGgz|$eF5p*Di;7Rc+ zbDbcyQz2+yLN^Fn&T7*1U|IESk9^t%&DS3KN4$x(Ekv7ZwDn@kgnMiTnTYMO`i-v8 zV?FW>AO$@5*$gy)Z70Sr;J1RX`E)7F?i$mJZ9X+uSzE%(!e%8A=UL+#Y6SpyTMyR6 z&I9W#WZ5WTr+BO<5#m3apjGsV7zsN~wk4}5hBV8IQ>@oj z2mAZ`=-RJPaao9aR!~&orIol*W^u%Xg-Oqozs0E+VQA z_Ptxe8G>++z=VagQmG&cpcn^TyqQ4qnMCjyNq16gjdhye^PK}#v4B8;6VQk+2g6F? z3JNuOt$zyajsiq@Qszp`2ArED5@}XQbp0ZrxD1O4# z5*7o$7Q*ozf#i0lPr8=0aL{6ppbs%(AY>#JoXP|`^BlZL-dT!jIoo5@zwroFCe=PrzZFQkwU}abbR{`jDVPR2JTgD&N7MiGf zOC72$5q3*LNf1$i4{3zR(}tmJ(B?=};<`CYwlbN`m-+HG1b%4uN3YjIt;y}2X>0@R z#!{o*!ODd0O@#g(cRDXZDl|Ui1|X0ICv;o{lke?`$2CT~uzPO6D4f^MTA38#^#@P{ zlNUXqA-<(*e2)WsFh6#!3#V@|cVpn^N}zssb=HgZxW4Ud|GbUJ?@Wiw?O&h@R?VT% zW*Y#Ylv*;QgL!3wZ6`PtAZAE!k2R$AI#;QAI<2ZHh|dJR;LDgJ*yY~TaQxk&C-*GU z=z%kOTRraD5dg+`t@Y45@1LDF6N7aja&uu?i578nNYto5Ebc9VAZZ>|R=g=W2tB9PAr?F{uT?RA592sWDJee-?|X}I6FH`|e>fTyJL zYtmoS&vz@?DD5P1-)d1LcihHLz(glE{MofJf(ptpo$4C$oKcY$dP>QrkHQ@SL{Nzb#?Chm}dVi z-Sn-uj(_Ls#Wl3Z9-ITon!PEP+U5L!Y2>25LW@V*Zg8}m=v>Y>xB`s?@Cno!dEerj z9!n@~`&xecn|a9W2+X=D@}jdX9Z-i_$qyi*c|{=jZFP0i)^(Kl{q+aNVMgrpv1#ajnC&VOE+XZa$^8WCxam)cbGS7MNxS%FY(c#R3UGM zw=}%B?qkM5V1OL|g6WBzERG`Qyp9A5c+Nn=n}rDin@qGvdC^!-C(cvZh7r`kww&hK1oj8;@<=a*8P8%5 zuRrZZ?Tl!QW@=x%C{hvMKT)Y|hM4#mL-C%+YFsM(Z71V)qq-$!e(b=e^xpRZ^)HSo z9$d0&Fi*vYXn!LxZ0SA1-*M|J9!qeh@i8Z?LjD^g)bEkLdESz5U)wP%%ppH)EjzMc zpz*8WK7nyw@k7?x1ZmCht(KP@%bqM;` z*BALJdDEnks#O9Pj|1J~#E<^bv_Fw1$2?EQBhd(6c;Qu`HG+l>)B_xi z&*7@>ke=dsZX?% zcKCb6M!eZW^QolbKo-U2vf|u*(BkN&P_ovUWV1cZVQaH4*QDsMcxIe8wHLA=0AEJ% zbub{jy=j=l4L9G*7_TwVW-Ehh+p}P2TA9et1iIF4aj%7%jDOp;Sw%8QBXs&*Z5{_b zfDLf*Xjl|Ft*~c%hR|*!JejiDb`;p!vMm5v)-JiZA&SO=`Zs|n<~=sr_(p!P5zE<1 poHmlK=jKKu2jhPlsL>K;7TfY^YzsN~cjigY5an6tMpA*F{RdGE+R^|3 literal 16990 zcmcheX>(jh6^8FusNz2irV392884}jB0xZvV?`-%SV~g)G^N!RmL(y{c7&6^4!lpl zbJX4U_DCbyP*bD3O!wKhKHd7yzkk*Jvb)uN+;JW0I_kdbmb$fWz5BiHH}pN}wshUs zcT<0N^yWx^ce>;5lkT)T)Zcg>bq9KX+P&4=s%^h{)Zn^tN*SpV5jUMAJ1FS6%E1DzJ7fwsOWT0^k(c2(=#KPhYbM#=zQ!+ z!oIC{n~A=l=7Hc^GHZ#NESXXFKyS>!hN#}t(?OEgj_y6bKiN@jEwyUr-TX&{qaVy6ou=Z@- z5uxxo?01yB>{NWCjT2E2c;Y&eOzq`(0(bjKVrUa9ve$!SFFhko;^>yf645NlY!M&U zSNDbGKoK+R+?ZN14a*?P5VsGdWqjzZq=2p1LJ##Oq(0xjvt(L+4%Ag)*pe){|19}3 z(6FtS$>y-cwHXBu5=~&mUf>7KF!P@7PBaP*d481aXrvr)H)Vm_ivD-HPg737ZoU=F zrGfr#r8m%=^B$ue`{8?2lo1KGbdUEsM(pY>TFqIFD7vrbSNi22iOQTk8AZg~(rm}O zY)i&=qTi47fjh=nYbSc+80i>Y$6c}xF`Jz1d;&dV@%dC=VjFx}A1y5Q?IC4fRCCJt zw zv+n0=)Q0?STexBr-UuDoM#!~41IqBmjQ=E!#~O*?MBRE0`4xYPIaC0L()m=Qx6@o~ zY1jH!ai)f>WwR*@ODH|l~Z3dH~TXU`q$O&ezI%x)c$KahIUD$=u58cLGTv1OA4u$Tz_r~*0D5bTG1hv zhQ}@Pxf+FitavyPn)(lnfxV>E_VMPNLR;;k^kp8dMrl-1we8k+SW+3#iQr|#K(^($ zScBLqwv;l$x_x1uU)DnD?10jt+EWO{qhMUo9T zId=v>!xhqVVWesx0}_{>>o56+`ioit3A-LcMy@nUnpu&#?iep@jal3IF2lK;>Jq=b zw9H52S(L~T1lx2J^l0m{mf#RhnP)8>!$BrQMN2D7FUPHtzB+$iMBBw4Q^PU#+Uk)T z@dl0Y_d3*4g<=h?T8XMeKddjH)iTT4>)!<&(^<>5jL<=`?eeWKIrCBhx@sw9>5Q~3 z>LT-9EVDjU@f>hWU0aIOL1aAY(^!*uMRc5`Q+s_xMAm-U+WK*{Wp_c7uVmh^t$-Qz zdM!oPXsj1=olWF`qf+}>digHrO;#CTBa2d7lHaKS@N^)Tb4=M&RmErejoOs@@Udo; z`RPQmx!6W7;_#>k-BwY zd7N^?OFcoi*ATU5lpHu+m| zw_#&w$a1!>kRSOMJBteQtYPJA8n-WPy*~wL2dxu#h$ToK$k?`Pz+qfiJI@x^#UK2? zO8#8?c)w;YGYA<9J^odmF{JBzQS7RPao3@(rYl!r^c3zlFg~1lEi3OcVYHRUs%#PO z4UE(iRAPoydSLh@6$qN~pYdr5d(Jv?%?y9Lc)l8%Y%_!7{HS zQ|#S7==HmMK&#!&6q%xW1$rOzvt)}@rq{)L5oY9>F|_R+{+}jmCqHjpn~oO$BgLiY z;pKX7OH{>5z%d(pUlkQt6Y7XWi~T zPk;X)=<`2VgKIW-jp}YT^pY*ubAdi;4rUDdTJCJ*F4ja6sr@==C@K77>bu>(fId1% zJG0CKZugpZlAk~ao|fy)k5v&NKfd_EKc#Q>kEn}ps*89laq~*E;c24Y+lk(PH0vQT zt6XPNKR!>e-UtugNza-7wL9li`O_1>|29h`u&uHE4@nNBkR~81~Y#Ev<{n`}%&7#!k0Ip5Y6L1B9bTQkXLUmCU}uE7gbuyul+=VT z;ZH+a*vl&^DDO>;_dRzLF0U{m^OBd*m3=LI1>Dg+=Dy$I$+4{LB}FAiZ-^Y&?kk06 zO8!hGo{q&JC-w#0wPY8O`1?9hTFwd!n92%WRk*)%zc(LixS>6ayNdb~#eRS9NT;9b z&F6Y^Tff)6@(epyM=iK_iuf3EH#dE0L??rKlNjZF=%KGu7uS^z`XNr2o$9t6JSzHn z_JjPcPmwt;A(?XQ&$Tig_1N8v9jjW_Wp=K+jah>I8R#Q+-t8w;MYa5gRJCj9tet-4 zn(^l`?lM{~SErz?Bx`>edThpC&Vut{yqah~Ek~#MazG~i-L=~0j5Y6sU^(vZU{&7R zh_30fe2N~BcSFdqWKVLx-<28&EJ1rb&$~_)Q@p+!=6B}QcE)LoT&vjDu^HCkmaNzL zxj#}@u`Noe$;d<0gMU7kIcIUc6#o2~CVuN~Oi8mT?RTyEE4d|Qs8vBlb~UW`qpjaN zI;rZVHf{YmAD*hZJ5p0g-TE$PuEou)jIwb;6w&a5i>BKG~PTT3sh7q7Tu z4T{w{QoMY;o zT*913g=dr1$=uGE^?qGY6Jwkeh`4QAc3k>VvU1%^zt88_wkjTsnYhDP+gTlC|v zM~eIrEvQ9<(kvTzA|FhXee-@QD>+hxaXp!>#q{F^j3TS%Q5kaPXnq%~pCjhkREsN9 z0R`l>IMYx@E$8vbn!`Hsa>>=1^N}=Do}bA1llaORj)?RjE7OQZp~b!Q#VKrY9Z}?- zq`s>Uc3kt}Cr2vpYjd7&cjhUk2#uJkI73E7R6=||Du&QqJ##ot=S<$4}@gP#*h)>!PmXE~qF*77x;mJMXf z$1~e8D}280PUGWv|C+AMT~~jQSm-*?o)3lYNat9ECgMGHGPQJ2-da!h^%dQ5=D`vD zx8SqpuzZ5g{&RUwcuO$AL?u8zs3p;#J%ki1d}5Z*JBN1-t>}GEQCMe>RU5CXmqOPi zW2fUxRnN*ENyjO8WbXZ5cYe_sJqmYQy$_tB5MS&WpA@he#q5apb*~k^!A^(MHH+7S z!yd9*2)2CW!l%7vjh@t3vwF=^2zdSLLs`L!WbPBtoCdmi z&NaIpSjRKFXV z5ft9-#qh~^R@^Pf(G7_mAM=GZb2=s>&_ojS*)S|3C=K2C8Y3o(;dBVlFjiaZW!~)m zIMWd@+ZY3Xi!V#8%zAK2MHCfeUxNKzuf?Ex|0C}+`8*LPRw8b;`kObBuGmSVADjPA zh+Vct>F_$_RMq*Hvp#I*4LD0Pjk!u3FQ^CP*49P9BMM?4ROICK1AXzqV?~NDlC2RP z_eIl=_By-+;WhkNCwu$WfG-Z;THh@VM6{xEqDFjP#jB{U!*20JM@&~Z#Il?E{!ZUM zmlJVxm}(JLbnKyE>+|fjMVoET-WK(>*R8qFi6r3J`>2eq^P_7r@0LX#CTbo@vYg0> zEEE6XLZ&W#yga!Ht`?nB`8IR%=}vpw@NLAcMSf{Vv+&XOwMCt!Nc1RI&AHc&*H~_x zz&}gU#2(``8L@}VO{S%ai5Sm*F%_D-fQG%HRW`9S>XBSWg1_7)0wepf?hLi)HpT0L zPZel-$LE<8ozLHg<^K@xq}`6&=N8w((XRn6&i~$%YRAqN^ou*#&ZfwLst^e~$8dHY zY34I=@QS}#;<@^1;mp-dhD<~Q_BWCuo=9DVFJh(CJh_5sS4?4RKwP};h+X)Y9oH7s z*+zX@Ijl1(Ia}z(_7a(wZ0U0;@HWi6@%y;x%RyyCylL9$=fBUE6#Mv(vZw~pN{g3( zP)3D`AEw3$n$oi2qS@t@-jwBNW zck{3?+oLCf;O>mqEG55j#(jR?sck8uL!Dh}D#xh~irv;nlEsR0&Wd)fIfwC7_odF3 zBq{#8ke%&5FLgy+%E)$Dt~D-OJC?KadaHzB53B!|3rXXD@AR#$$h4JE!#B$Rl5w{q z*68(_Vd@>?2VIGBUF#@AT~R{MG`G5s^Jveo@B%W#yc7EGDr}cYKA)Q6eUm{OeW4*H(t zxREvDIKl2noIp1fL}zx<=;u=b*OjhoGU_$writeLogFile ("Gateway JOIN: no candidate matchers promoted, for client [%llu] requesting world: %d\n", in_msg.from, world_id); + } + else + { + storeRequestingClient (world_id, in_msg.from); + } + } + // if origin matcher for the requested world exists, reply the client + else + { + id_t matcher_id = _origins[world_id]; + + if (_matchers.find (matcher_id) == _matchers.end ()) + { + LogManager::instance ()->writeLogFile ("Gateway JOIN: origin matcher [%llu] cannot be found for world [%u]\n", matcher_id, world_id); + + if (matcher_id == 0) + { + // TODO: find what causes this + LogManager::instance ()->writeLogFile ("Gateway JOIN: matcher_id = 0, should not happen, remove it\n"); + _origins.erase (world_id); + } + break; + } + + // if the matcher is still in the process of being promoted, record client request + if (_matchers[matcher_id].state == PROMOTING) + { + storeRequestingClient (world_id, in_msg.from); + break; + } + + // send reply to joining client + // NOTE: that we send directly back, as this is a gateway response + Message msg (NOTIFY_MATCHER); + msg.priority = 1; + msg.msggroup = MSG_GROUP_VAST_CLIENT; + msg.store (_matchers[matcher_id].addr); + msg.addTarget (in_msg.from); + sendMessage (msg); + + printf ("Gateway JOIN replies [%llu] with origin matcher [%llu] on world (%u)\n", in_msg.from, _origins[world_id], world_id); + } + */ + } + break; + + case VAST.msgtype.SUB: { + + // NOTE: we allow sending SUBSCRIBE for existing subscription if + // the subscription has updated (for example, the relay has changed) + + LOG.debug('[' + _self.id + '] VASTMatcher SUBSCRIBE from [' + from_id + ']'); + + // extract subscription + var sub = new VAST.sub; + sub.parse(pack.msg); + + /* + // query the closest matcher + Voronoi *voronoi = _VSOpeer->getVoronoi (); + + id_t closest = voronoi->closest_to (sub.aoi.center); + + // check if we should accept this subscription or forward + if (voronoi->contains (_self.id, sub.aoi.center) || + closest == _self.id) + { + // slight increase AOI radius to avoid client-side ghost objects + sub.aoi.radius += SUBSCRIPTION_AOI_BUFFER; + + // assign a unique subscription number if one doesn't exist, or if the provided one is not known + // otherwise we could re-use a previously assigned subscription ID + // TODO: potentially buggy? (for example, if the matcher that originally assigns the ID, leaves & joins again, to assign another subscription the same ID?) + // TODO: may need a way to periodically re-check ID with the assigning entry point + //if (sub.id == NET_ID_UNASSIGNED || _subscriptions.find (sub.id) == _subscriptions.end ()) + // IMPORTANT NOTE: we assume a provided subscription ID is good, and re-use it + // if we only re-use if the ID already exists in record (e.g. check existence in _subscriptions) + // then must make sure the client app also updates the subID, otherwise the movement would be invalid + if (sub.id == NET_ID_UNASSIGNED) + sub.id = _net->getUniqueID (ID_GROUP_VON_VAST); + else + printf ("VASTMatcher [%llu] using existing ID [%llu]\n", in_msg.from, sub.id); + + // by default we own the client subscriptions we create + // we may simply update existing subscription when clients re-subscribe due to matcher failure + map::iterator it = _subscriptions.find (sub.id); + if (it == _subscriptions.end ()) + addSubscription (sub, true); + else + { + bool is_owner = true; + + updateSubscription (sub.id, sub.aoi, 0, &sub.relay, &is_owner); + } + + // notify relay of the client's subscription -> hostID mapping + Message msg (SUBSCRIBE_NOTIFY); + msg.priority = 1; + msg.msggroup = MSG_GROUP_VAST_RELAY; + msg.store (sub.id); + msg.store (sub.host_id); + msg.addTarget (sub.relay.host_id); + sendMessage (msg); + + // send back acknowledgement of subscription to client via the relay + // NOTE: acknowledge should be sent via relay in general, + // as the subscribe request could be forwarded many times + msg.clear (SUBSCRIBE_R); + msg.priority = 1; + + // store both the assigned subscription ID, and also this matcher's address + // (so the client may switch the current matcher) + msg.store (sub.id); + msg.store (_self.addr); + + msg.addTarget (sub.id); + sendClientMessage (msg); + + + // if the client connects directly to me, send reply directly + //if (sub.host_id == in_msg.from) + // sendClientMessage (msg, in_msg.from); + //otherwise send via its relay + //else + //{ + // msg.addTarget (sub.id); + // sendClientMessage (msg); + //} + + + // erase closest matcher record, so that the subscribing client will be notified again + // this occurs when the client is re-subscribing to a substitute matcher in case of its current matcher's failure + if (_closest.find (sub.id) != _closest.end ()) + _closest.erase (sub.id); + + // record the subscription request + LogManager::instance ()->writeLogFile ("VASTMatcher: SUBSCRIBE request from [%llu] success\n", in_msg.from); + } + else + { + // forward the message to neighbor closest to the subscribed point + in_msg.reset (); + in_msg.targets.clear (); + in_msg.addTarget (closest); + + sendMessage (in_msg); + } + */ + } + break; + + default: + // packet unhandled + LOG.debug('VAST Matcher: message unprocessed'); + return false; + break; + } + + // successfully handle packet + return true; + } + + var _connHandler = this.connHandler = function (id) { + LOG.debug('VAST matcher [' + id + '] connected'); + } + + var _disconnHandler = this.disconnHandler = function (id) { + LOG.debug('VAST matcher [' + id + '] disconnected'); + + // generate a BYE message + var pack = new VAST.pack( + VAST.msgtype.BYE, + {}, + VAST.priority.HIGHEST); + + _packetHandler(id, pack); + } + + ///////////////////// + // msg_handler methods + // + + var _that = this; + + // function to create a new net layer + this.init = function (self_id, port, done_CB) { + + port = port || VAST_DEFAULT_PORT; + + // create new layer + var m_handler = new msg_handler(self_id, port, function (local_addr) { + + // NOTE: this will cause initStates() be called + m_handler.addHandler(_that); + + // notify done + if (typeof done_CB === 'function') + done_CB(local_addr); + }); + } + + var _initStates = this.initStates = function (msg_handler) { + + if (msg_handler !== undefined) { + + _msg_handler = msg_handler; + + var id = _msg_handler.getID(); + LOG.warn('VAST_matcher initStates called with msg_handler, id: ' + id); + + // add convenience references + _storeMapping = _msg_handler.storeMapping, + _getID = _msg_handler.getID, + _disconnect = _msg_handler.disconnect, + _sendMessage = _msg_handler.sendMessage, + _sendPack = _msg_handler.sendPack; + + // add VON peer as self node + _self = new VON.peer(); + _msg_handler.addHandler(_self); + } + } + + ///////////////////// + // constructor + // + LOG.debug('VAST constructor called'); + + // + // connect with handler + // + + var _msg_handler; + + // convenience references + var _storeMapping, _getID, _disconnect, _sendMessage, _sendPack; + + // + // private variables + // + + // state of joining + var _state = VAST.state.ABSENT; + + // reference to self node (a VON peer) + var _self; + +} + +// export the class with conditional check +if (typeof module !== "undefined") + module.exports = VAST_matcher; diff --git a/VON_peer.js b/VON_peer.js index a669b771..63185fdb 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -37,10 +37,12 @@ vast_net (see definition in vast_net.js) // constructor - VON_peer(id, port, aoi_buffer, aoi_use_strict) + VON_peer(aoi_buffer, aoi_use_strict) // basic functions - join(addr, aoi, done_CB) join a VON network with a given gateway (entry) + init(id, port, addr, done_CB) init a VON peer with id, listen port & gateway's address + query(center, acceptor_CB) find the acceptor for a given center point + join(aoi, done_CB) join a VON network with a given aoi leave() leave the VON network move(aoi, send_time) move the AOI to a new position (or change radius) list() get a list of AOI neighbors @@ -77,27 +79,29 @@ var NONOVERLAP_MULTIPLIER = 1.25; // multiplier for aoi buffer for determi var TICK_INTERVAL = 100; // interval (in milliseconds) to perform tick tasks // flags -var OVERLAP_CHECK_ACCURATE = false; // whether VON overlap checks are accurate +var OVERLAP_CHECK_ACCURATE = false; // whether VON overlap checks are accurate // enumation of VON message -// TODO: combine DISCONNECT & BYE? var VON_Message = { - VON_DISCONNECT: 0, // VON's disconnect - VON_QUERY: 1, // VON's query, to find an acceptor that can take in a joining node - VON_NODE: 2, // VON's notification of new nodes - VON_HELLO: 3, // VON's hello, to let a newly learned node to be mutually aware - VON_HELLO_R: 4, // VON's hello response - VON_EN: 5, // VON's enclosing neighbor inquiry (to see if my knowledge of EN is complete) - VON_MOVE: 6, // VON's move, to notify AOI neighbors of new/current position - VON_MOVE_F: 7, // VON's move, full notification on AOI - VON_MOVE_B: 8, // VON's move for boundary neighbors - VON_MOVE_FB: 9, // VON's move for boundary neighbors with full notification on AOI - VON_BYE: 10 // VON's disconnecting a remote node + VON_BYE: 0, // VON's disconnect + VON_PING: 1, // VON's ping, to check if a connected neighbor is still alive + VON_QUERY: 2, // VON's query, to find an acceptor to a given point + VON_JOIN: 3, // VON's join, to learn of initial neighbors + VON_NODE: 4, // VON's notification of new nodes + VON_HELLO: 5, // VON's hello, to let a newly learned node to be mutually aware + VON_HELLO_R: 6, // VON's hello response + VON_EN: 7, // VON's enclosing neighbor inquiry (to see if my knowledge of EN is complete) + VON_MOVE: 8, // VON's move, to notify AOI neighbors of new/current position + VON_MOVE_F: 9, // VON's move, full notification on AOI + VON_MOVE_B: 10, // VON's move for boundary neighbors + VON_MOVE_FB: 11 // VON's move for boundary neighbors with full notification on AOI }; var VON_Message_String = [ - 'VON_DISCONNECT', + 'VON_BYE', + 'VON_PING', 'VON_QUERY', + 'VON_JOIN', 'VON_NODE', 'VON_HELLO', 'VON_HELLO_R', @@ -105,27 +109,9 @@ var VON_Message_String = [ 'VON_MOVE', 'VON_MOVE_F', 'VON_MOVE_B', - 'VON_MOVE_FB', - 'VON_BYE' + 'VON_MOVE_FB' ]; -// TODO: msg priority is probably not VON-specific -var VON_Priority = { - HIGHEST: 0, - HIGH: 1, - NORMAL: 2, - LOW: 3, - LOWEST: 4 -}; - -// TODO: node state is probably not VON-specific -var NodeState = { - ABSENT: 0, - QUERYING: 1, // finding / determing certain thing - JOINING: 2, // different stages of join - JOINED: 3, -}; - // status on known nodes in the neighbor list, can be either just inserted / deleted / updated var NeighborUpdateStatus = { INSERTED: 1, @@ -141,71 +127,127 @@ var NeighborState = { NEIGHBOR_ENCLOSED: 2 }; +// +// NOTE: this class interface with msg_handler via the following: +// _connHandler, _disconnHandler, _packetHandler, _self_id +// it also uses the following provided by msg_handler +// sendMessage, sendPack, net +// + // definition of a VON peer -function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { - +function VONPeer(l_aoi_buffer, l_aoi_use_strict) { + ///////////////////// // public methods // - // join a VON network with a given gateway (entry) - var _join = this.join = function (GW_addr, aoi, done_CB) { + var _that = this; + + // function to create a new net layer + this.init = function (self_id, port, GW_addr, done_CB) { + + self_id = self_id || VAST_ID_UNASSIGNED; + port = port || VON_DEFAULT_PORT; + + LOG.debug('VON_peer init called, self_id: ' + self_id + ' port: ' + port); + + // create new layer + _msg_handler = new msg_handler(self_id, port, function (local_addr) { + + // NOTE: this will cause initStates() be called + _msg_handler.addHandler(_that); + + // store gateway address, ensure input conforms to internal data structure + // NOTE: need to do it here, as _storeMapping is not effective after addHandler calls initStates + var addr = new VAST.addr(); + addr.parse(GW_addr); + _storeMapping(VAST_ID_GATEWAY, GW_addr); + + LOG.warn('VON_peer init: gateway set to: ' + addr.toString()); + + // store callback after gateway response + _init_done_CB = done_CB; + + // send PING to gateway to learn of my id + if (self_id === VAST_ID_UNASSIGNED) + _sendMessage(VAST_ID_GATEWAY, VON_Message.VON_PING, {request: true}, VAST.priority.HIGHEST); + else + _setInited(); + }); + } + + // find the acceptor for a given center point + var _query = this.query = function (contact_id, center, msg_type, msg_para) { + + LOG.debug('VON_peer query() will contact node[' + contact_id + '] to find acceptor'); + + var msg = { + pos: center, + type: msg_type, + para: msg_para + } + // send out query request + _sendMessage(contact_id, VON_Message.VON_QUERY, msg, VAST.priority.HIGHEST); + } + + // join a VON network with a given aoi + var _join = this.join = function (aoi, done_CB) { + + if (_state === VAST.state.ABSENT) { + LOG.error('VON_peer.join(): node not yet init'); + return; + } + // check if already joined - if (_state === NodeState.JOINED) { + if (_state === VAST.state.JOINED) { LOG.warn('VON_peer.join(): node already joined'); if (typeof done_CB === 'function') done_CB(_self.id); return; } - // ensure function input conforms to internal data structure - var addr = new VAST.addr(); - addr.parse(GW_addr); - LOG.debug('VON_peer join() called, joining: ' + addr.toString()); + LOG.debug('VON_peer join() called'); // change internal state - _state = NodeState.JOINING; + _state = VAST.state.JOINING; // keep reference to call future once join is completed _join_done_CB = done_CB; // set self AOI _self.aoi.update(aoi); - - LOG.debug('calling getHost()'); + + // update address info for self + var addr = _msg_handler.getAddress(); + _self.endpt = new VAST.endpt(addr.host, addr.port); + + // store self node to neighbor map + _insertNode(_self); - // create self object - _net.getHost(function (local_IP) { + // if I'm gateway, no further action required + // TODO: doesn't look clean, can gateway still send query request to itself? + // that'll be a more general process + // (however, will deal with how to determined 'already joined' for gateway) + if (_self.id === VAST_ID_GATEWAY) + return _setJoined(); + + // build node info for acceptor to contact back (from _self) + var joiner = new VAST.node(); - LOG.debug('local IP: ' + local_IP); - - // update address info for self - _self.endpt = new VAST.endpt(local_IP, l_port); - - // return value is actual port binded - _net.listen(l_port, function (actual_port) { - // update port of self if different from the one attempting to bind - if (actual_port != l_port) - _self.endpt.addr.port = actual_port; - - // store self node to neighbor map - _insertNode(_self); - - // if I'm gateway, no further action required - // TODO: doesn't look clean, can gateway still send query request to itself? - // that'll be a more general process - // (however, will deal with how to determined 'already joined' for gateway) - if (_self.id === VAST_ID_GATEWAY) - return _setJoined(); - - // send out join request - // TODO: if id is not correct, remote host will send back correct one - _net.storeMapping(VAST_ID_GATEWAY, GW_addr); - _sendMessage(VAST_ID_GATEWAY, VON_Message.VON_QUERY, _self, VON_Priority.HIGHEST); - - }); - }); + //LOG.debug('joiner (before): ' + joiner.toString()); + + joiner.update(_self); + joiner.aoi.center = aoi.center; + + //LOG.debug('joiner (after): ' + joiner.toString()); + LOG.debug('joiner: ' + joiner.toString()); + + // send out query request first to find acceptor + _query(VAST_ID_GATEWAY, aoi.center, VON_Message.VON_JOIN, joiner); + + // TODO: if id is not correct, remote host will send back correct one + //_sendMessage(VAST_ID_GATEWAY, VON_Message.VON_QUERY, _self, VAST.priority.HIGHEST); } // leave the VON network @@ -311,7 +353,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var pack = new VAST.pack( VON_Message.VON_MOVE, node_info, - VON_Priority.HIGHEST); + VAST.priority.HIGHEST); // send to regular neighbors pack.targets = regular_list; @@ -357,7 +399,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // check if we've joined the VON network var _isJoined = this.isJoined = function () { - return (_state === NodeState.JOINED); + return (_state === VAST.state.JOINED); } // check if a given ID is an existing neighbor @@ -422,7 +464,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { if (_isNeighbor (id) == false) return false; - var timeout = period * _net.getTimestampPerSecond (); + var timeout = period * _net.getTimestampPerSecond (); return ((_tick_count - _neighbors[id].endpt.lastAccessed) < timeout); */ } @@ -440,7 +482,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { return false; // notify network layer about connection info, no need to actually connect for now - _net.storeMapping(node.id, node.endpt.addr); + _storeMapping(node.id, node.endpt.addr); // update last access time node.endpt.lastAccessed = UTIL.getTimestamp(); @@ -463,7 +505,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _updateStatus[node.id] = NeighborUpdateStatus.INSERTED; var meta_keys = node.hasOwnProperty('meta') ? Object.keys(node.meta).length : 0; - LOG.warn('[' + _self.id + '] insertNode neighbor (after insert) size: ' + Object.keys(_neighbors).length + + LOG.debug('[' + _self.id + '] insertNode neighbor (after insert) size: ' + Object.keys(_neighbors).length + ' voro: ' + _voro.size() + ' meta: ' + meta_keys); @@ -557,7 +599,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // set current node to be 'joined' var _setJoined = function () { - _state = NodeState.JOINED; + _state = VAST.state.JOINED; // notify callback if (typeof _join_done_CB === 'function') @@ -567,6 +609,19 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _interval_id = setInterval(_tick, TICK_INTERVAL); } + // set current node to be 'init' + var _setInited = function () { + + _state = VAST.state.INIT; + + // NOTE: other fields (endpt, aoi, time) may be empty at this point + _self = new VAST.node(_getID()); + + // notify done + if (typeof _init_done_CB === 'function') + _init_done_CB(_self.id); + } + var _sendKeepAlive = function () { // simply move a little to indicate keepalive @@ -626,11 +681,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { if (_isRelevantNeighbor (node, _self)) { LOG.debug('[' + node.id + '] is relevant to self [' + _self.id + ']'); - - // store new node as a potential neighbor, pending confirmation from the new node - // this is to ensure that a newly discovered neighbor is indeed relevant - //_potential_neighbors[target] = node; - + // add a new relevant neighbor // NOTE: we record first without inserting because we need to remove // the new neighbor positions from Voronoi first @@ -638,7 +689,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // notify mapping for sending Hello message // TODO: a cleaner way (less notifymapping call?) - _net.storeMapping(node.id, node.endpt.addr); + _storeMapping(node.id, node.endpt.addr); // send HELLO message to newly discovered nodes // NOTE that we do not perform insert yet (until the remote node has confirmed via MOVE) @@ -664,9 +715,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // NOTE: erase new neighbors seems to bring better consistency // (rather than keep to next round, as in previous version) _new_neighbors = {}; - - // TODO: _potential_neighbors should be cleared once in a while - + LOG.debug('total neighbors (after process neighbors): ' + Object.keys(_neighbors).length); LOG.debug('total voro nodes: ' + _voro.size() + '\n'); } @@ -864,7 +913,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { for (var i=0; i < list.length; i++) nodes.push(_getNode(list[i])); - _sendMessage(target, VON_Message.VON_NODE, nodes, VON_Priority.NORMAL, reliable); + _sendMessage(target, VON_Message.VON_NODE, nodes, VAST.priority.NORMAL, reliable); } // send a list of IDs to a particular node @@ -887,7 +936,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // prepare HELLO message // TODO: do not create new HELLO message every time? (local-side optimization) - _sendMessage(target, VON_Message.VON_HELLO, node, VON_Priority.HIGHEST, true); + _sendMessage(target, VON_Message.VON_HELLO, node, VAST.priority.HIGHEST, true); } // send a particular node its perceived enclosing neighbors @@ -895,7 +944,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { var id_list = _voro.get_en(target); if (id_list.length > 0) - _sendMessage(target, VON_Message.VON_EN, id_list, VON_Priority.HIGH, true); + _sendMessage(target, VON_Message.VON_EN, id_list, VAST.priority.HIGH, true); } var _sendBye = function (targets) { @@ -906,7 +955,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { LOG.debug('sendBye target size: ' + targets.length); - var pack = new VAST.pack(VON_Message.VON_BYE, {}, VON_Priority.HIGHEST); + var pack = new VAST.pack(VON_Message.VON_BYE, {}, VAST.priority.HIGHEST); pack.targets = targets; _sendPack(pack, true); @@ -925,55 +974,61 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // handlers for incoming messages, connect/disconnect // - var _handlePacket = function (from_id, pack) { + var _packetHandler = this.packetHandler = function (from_id, pack) { + + // if node is not initiated, do not process any message, unless it's a PING response + if (_state < VAST.state.INIT) { - // if join is not even initiated, do not process any message - if (_state == NodeState.ABSENT) { - LOG.error('node not yet join, should not process any messages'); - return false; + if (pack.type !== VON_Message.VON_PING) { + LOG.error('VON_peer: node not yet init, should not process any messages'); + return false; + } + + _setInited(); } LOG.debug('[' + _self.id + '] ' + VON_Message_String[pack.type] + ' from [' + from_id + '], neighbor size: ' + Object.keys(_neighbors).length); switch (pack.type) { - - // VON's query, to find an acceptor that can take in a joining node - case VON_Message.VON_QUERY: { + + // VON's ping, to check if a connected host is still alive + case VON_Message.VON_PING: { - // update the joiner's id (if it's a newly joined node) - if (pack.msg.id === VAST_ID_UNASSIGNED) - pack.msg.id = from_id; + // check if it's a request, respond to it + if (typeof pack.msg.request !== 'undefined' && pack.msg.request == true) { + LOG.warn('VON_PING receive request, simply respond'); + _sendMessage(from_id, VON_Message.VON_PING, {request: false}, VAST.priority.HIGH, true); + } + } + break; + // VON's query, to find an acceptor to a given point + case VON_Message.VON_QUERY: { + // extract message - var joiner = new VAST.node(); - joiner.parse(pack.msg); - LOG.debug('joiner: ' + joiner.toString()); - - // TODO: verify message in a systematic way - if (joiner.endpt.addr.isEmpty()) { - LOG.error('joiner has no valid IP/port address, ignore request'); - break; - } - + var pos = new VAST.pos(); + pos.parse(pack.msg.pos); + LOG.debug('VON_QUERY checking pos: ' + pos.toString()); + // find the node closest to the joiner - var closest = _voro.closest_to(joiner.aoi.center); - - LOG.debug('closest node found: ' + closest); - + var closest = _voro.closest_to(pos); + LOG.debug('closest node: ' + closest + ' (' + typeof closest + ')'); + + // TODO: should generate a VON_JOIN directly to self + /* // if this is gateway receiving its own request if (closest === VAST_ID_UNASSIGNED && Object.keys(_neighbors).length === 1) { LOG.warn('gateway getting its own VON_QUERY, return empty list'); _sendNodes (from_id, [], true); break; } - - LOG.debug('closest node: ' + closest + ' (' + typeof closest + ')'); - + */ + // forward the request if a more appropriate node exists // TODO: contains() might recompute Voronoi, isRelevantNeighbor below // also will recompute Voronoi. possible to combine into one calculation? - if (_voro.contains (_self.id, joiner.aoi.center) === false && - _isSelf (closest) === false && + if (_voro.contains(_self.id, pos) === false && + _isSelf(closest) === false && closest != from_id) { LOG.warn('forward VON_QUERY request to: ' + closest); @@ -984,69 +1039,97 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _sendPack(pack); } else { - - // insert first so we can properly find joiner's EN - _insertNode (joiner); - // I am the acceptor, send back initial neighbor list - var list = []; + LOG.warn('accepting VON_QUERY from: ' + from_id); + + // I'm the acceptor, re-direct message content to self + pack.type = pack.msg.type; + pack.msg = pack.msg.para; + pack.targets = []; + pack.targets.push(_self.id); - // loop through all my known neighbors and check which are relevant to the joiner to know - var out_str = ''; - for (var id in _neighbors) { + // add from_id as this is not found elsewhere + // TODO: not clean to add to pack? + //pack.from_id = from_id; - var neighbor = _neighbors[id]; - //LOG.debug('checking if neighbor [' + neighbor.id + '] should be notified'); - // store only if neighbor is both relevant and timely - // TODO: more efficient check (EN of joiner is calculated multiple times now) - - var is_relevant = _isRelevantNeighbor(neighbor, joiner); - //LOG.debug('is relevant: ' + is_relevant); - if (neighbor.id != joiner.id && - is_relevant && - _isTimelyNeighbor(neighbor.id)) { - list.push(neighbor.id); - out_str += neighbor.id + ' '; - } - } + LOG.debug('redirect to self: ' + JSON.stringify(pack)); - LOG.debug('notify ' + list.length + ' nodes to joiner: ' + out_str); - if (list.length <= 1) { - LOG.warn('too few neighbors are notified! check correctness\n'); - LOG.debug(_voro.to_string()); - } + // NOTE: need to check if this works correctly, to send back to self + //_sendPack(pack); - // send a list of nodes to a specific target node - _sendNodes (joiner.id, list, true); + // NOTE: has danger to enter infinite loop + // TODO: make some kind of msgqueue for sending? + _packetHandler(from_id, pack); } } break; + + // VON's join, to learn of initial neighbors + case VON_Message.VON_JOIN: { - // process notifications for new nodes - case VON_Message.VON_NODE: { + // update the joiner's id (if it's a newly joined node) + if (pack.msg.id === VAST_ID_UNASSIGNED) + pack.msg.id = from_id; + + // extract message + var joiner = new VAST.node(); + joiner.parse(pack.msg); + LOG.debug('joiner: ' + joiner.toString()); - // if VON_NODE is received, we're considered joined - // NOTE: do this first as we need to update our self ID for later VON_NODE process to work - //LOG.warn('checking joining state: ' + _state); - if (_state === NodeState.JOINING) { - - // check if we're getting new ID - // TODO: is this clean? consider use VON_HELLO first when contacting new node/neighbor? - var selfID = _net.getID(); - LOG.debug('selfID, prev: ' + _self.id + ' new: ' + selfID); + // TODO: verify message in a systematic way + if (joiner.endpt.addr.isEmpty()) { + LOG.error('joiner has no valid IP/port address, ignore request'); + break; + } + + // insert first so we can properly find joiner's EN + _insertNode(joiner); + + // if this is gateway receiving its own request + // TODO: to check correctness + if (Object.keys(_neighbors).length === 1) { + LOG.warn('gateway getting its own VON_QUERY, return empty list'); + _sendNodes(from_id, [], true); + break; + } + + // I am the acceptor, send back initial neighbor list + var list = []; + + // loop through all my known neighbors and check which are relevant to the joiner to know + var out_str = ''; + for (var id in _neighbors) { + + var neighbor = _neighbors[id]; + //LOG.debug('checking if neighbor [' + neighbor.id + '] should be notified'); + // store only if neighbor is both relevant and timely + // TODO: more efficient check (EN of joiner is calculated multiple times now) - // update self - // NOTE: we don't use _updateNode as it requires the same node id - _deleteNode(_self.id); - _self.id = selfID; - _insertNode(_self); - - // NOTE: we don't notify join success until neighbor list is processed - // so that upon join success, the client already has a list of neighbors - } - - var nodelist = pack.msg; - + var is_relevant = _isRelevantNeighbor(neighbor, joiner); + //LOG.debug('is relevant: ' + is_relevant); + if (neighbor.id != joiner.id && + is_relevant && + _isTimelyNeighbor(neighbor.id)) { + list.push(neighbor.id); + out_str += neighbor.id + ' '; + } + } + + LOG.debug('notify ' + list.length + ' nodes to joiner: ' + out_str); + if (list.length <= 1) { + LOG.warn('too few neighbors are notified! check correctness\n'); + LOG.debug(_voro.to_string()); + } + + // send a list of nodes to a specific target node + _sendNodes(joiner.id, list, true); + } + break; + + // process notifications for new nodes + case VON_Message.VON_NODE: { + + var nodelist = pack.msg; for (var i=0; i < nodelist.length; i++) { // extract message @@ -1077,10 +1160,15 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { } } // end going through node list - // process new neighbors if we just joined, - // otherwise, do this periodically & collectively during tick - if (_state === NodeState.JOINING) { - _contactNewNeighbors(); + // if VON_NODE is received for the first time, we're considered joined + if (_state === VAST.state.JOINING) { + + // process new neighbors if we just joined, + // otherwise, do this periodically & collectively during tick + _contactNewNeighbors(); + + // NOTE: we don't notify join success until neighbor list is processe + // so that upon join success, the client already has a list of neighbors _setJoined(); } } @@ -1122,7 +1210,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { if (Object.keys(_meta).length > 0) res_obj.meta = _meta; - _sendMessage(from_id, VON_Message.VON_HELLO_R, res_obj, VON_Priority.HIGH, true); + _sendMessage(from_id, VON_Message.VON_HELLO_R, res_obj, VAST.priority.HIGH, true); // check if enclosing neighbors need any update _checkConsistency(from_id); @@ -1150,7 +1238,7 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _updateNode(neighbor); } else - LOG.warn('got VON_HELLO_R from unknown neighbor [' + from_id + ']'); + LOG.warn('[' + _self.id + '] got VON_HELLO_R from unknown neighbor [' + from_id + ']'); } break; @@ -1247,17 +1335,16 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { break; // VON's disconnecting a remote node - case VON_Message.VON_DISCONNECT: case VON_Message.VON_BYE: { // TODO: check if from_id is certainly the sending node's ID if (_neighbors.hasOwnProperty(from_id)) { - _checkConsistency (from_id); - _deleteNode (from_id); + _checkConsistency(from_id); + _deleteNode(from_id); } - // TODO: physically disconnect the node? (or it will be done by the remote node?) - var result = _net.disconnect(from_id); + // physically disconnect the node (or it will be done by the remote node) + var result = _disconnect(from_id); LOG.debug('disconnect succcess: ' + result); LOG.debug('after removal, neighbor size: ' + Object.keys(_neighbors).length); } @@ -1272,14 +1359,49 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // successfully handle packet return true; } - + + var _connHandler = this.connHandler = function (id) { + LOG.debug('VON peer [' + id + '] connected'); + } + + var _disconnHandler = this.disconnHandler = function (id) { + LOG.debug('VON peer [' + id + '] disconnected'); + + // generate a VON_BYE message + var pack = new VAST.pack( + VON_Message.VON_BYE, + {}, + VAST.priority.HIGHEST); + + _packetHandler(id, pack); + } + + ///////////////////// + // msg_handler methods + // + // clean up all internal states for a new fresh join - var _initStates = function () { + var _initStates = this.initStates = function (msg_handler) { LOG.debug('initStates called'); + if (msg_handler !== undefined) { + + _msg_handler = msg_handler; + + var id = _msg_handler.getID(); + LOG.warn('VON_peer initStates called with msg_handler, id: ' + id); + + // add convenience references + _storeMapping = _msg_handler.storeMapping, + _getID = _msg_handler.getID, + _disconnect = _msg_handler.disconnect, + _sendMessage = _msg_handler.sendMessage, + _sendPack = _msg_handler.sendPack; + } + // node state definition - _state = NodeState.ABSENT; + _state = VAST.state.ABSENT; // list of AOI neighbors (map from node id to node) _neighbors = {}; @@ -1291,7 +1413,6 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _neighbor_states = {}; _updateStatus = {}; - //_potential_neighbors = {}; _req_nodes = {}; _time_drop = {}; @@ -1303,75 +1424,48 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { _stat[VON_Message.VON_NODE] = new VAST.ratio(); // clear meta data - _meta = {}; + _meta = {}; } - - var _connHandler = function (id) { - LOG.debug('VON peer [' + id + '] connected'); - } - - var _disconnHandler = function (id) { - LOG.debug('VON peer [' + id + '] disconnected'); - - // generate a VON_DISCONNECT message - var pack = new VAST.pack( - VON_Message.VON_DISCONNECT, - {}, - VON_Priority.HIGHEST); - - _handlePacket(id, pack); - } - + ///////////////////// // constructor // LOG.debug('VON_peer constructor called'); - - // call parent class's constructor - //this.init(_connHandler, _disconnHandler, _handlePacket, l_self_id); - msg_handler.call(this, _connHandler, _disconnHandler, _handlePacket, l_self_id); - - // make a local reference to the parent class (msg_handler)'s net object - // need to create reference here because within functions (such as callbacks) - // 'this' refers to the function itself - - // TODO: a cleaner approach? - var _net = this.net; - var that = this; - - // convenience functions with the right executation context (the desirable 'this') - var _sendMessage = function () { - that.sendMessage.apply(that, arguments); - } - - var _sendPack = function () { - that.sendPack.apply(that, arguments); - } - - //LOG.warn('_net: ' + _net + ' this.net: ' + this.net); + + // + // constructor actions + // // set default AOI buffer size - if (l_aoi_buffer === undefined) - l_aoi_buffer = AOI_DETECTION_BUFFER; + l_aoi_buffer = l_aoi_buffer || AOI_DETECTION_BUFFER; // check whether AOI neighbor is defined as nodes whose regions // are completely inside AOI, default to be 'true' - if (l_aoi_use_strict === undefined) - l_aoi_use_strict = true; - - if (l_port === undefined) - l_port = VON_DEFAULT_PORT; + l_aoi_use_strict = l_aoi_use_strict || true; + + // + // handler-related + // + + // register with an existing or new message handler + // NOTE: to register, must provide: connHandler, disconnHandler, packetHandler + var _msg_handler = undefined; - // reference to self - // NOTE: other info of 'self' may be empty at this moment (e.g., endpt, aoi, etc.) - var _self = new VAST.node(l_self_id); - + // convenience references + var _storeMapping, _getID, _disconnect, _sendMessage, _sendPack; + // // internal states // - + + // reference to self (NOTE: need to be initialized in init()) + var _self; + // callback to use once join is successful var _join_done_CB = undefined; + + // callback to use once init is successful (got self ID from server) + var _init_done_CB = undefined; // interval id for removing periodic ticking var _interval_id = undefined; @@ -1385,7 +1479,6 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // TODO: combine these structures into one? var _new_neighbors; // nodes worth considering to connect (learned via VON_NODE messages) var _neighbor_states; // neighbors' knowledge of my neighbors (for neighbor discovery check) - //var _potential_neighbors; // neighbors to be discovered var _req_nodes; // nodes requesting neighbor discovery check var _updateStatus; // status about whether a neighbor is: 1: inserted, 2: deleted, 3: updated @@ -1400,16 +1493,11 @@ function VONPeer(l_self_id, l_port, l_aoi_buffer, l_aoi_use_strict) { // app-specific meta data var _meta; - - // clean all states - _initStates(); - -} // end of peer -VONPeer.prototype = new msg_handler(); +} // end of peer // NOTE: it's important to export VONPeer after the prototype declaration // otherwise the exported method will not have unique msg_handler instance // (that is, all msg_handler variables will become singleton, only one instance globally) if (typeof module !== 'undefined') - module.exports = VONPeer; + module.exports = VONPeer; \ No newline at end of file diff --git a/VSO_peer.js b/VSO_peer.js index bcb8b2dc..23f200e4 100644 --- a/VSO_peer.js +++ b/VSO_peer.js @@ -70,6 +70,7 @@ history: 2010/03/18 adopted from ArbitratorImpl.h in VASTATE 2012/09/09 initial js version (from VSOPeer.h) + 2012/11/01 converted to first js version */ require('./common.js'); @@ -202,7 +203,7 @@ class EXPORT VSOPeer : public VONPeer public: VSOPeer (id_t id, VONNetwork *net, VSOPolicy *policy, length_t aoi_buffer = AOI_DETECTION_BUFFER); - ~VSOPeer (); + ~VSOPeer (); // perform joining the overlay, indicate the joining position, AOI, contact origin, also whether dynamic adjustment is enabled void join (Area &aoi, Node *origin, bool is_static = false); @@ -348,7 +349,7 @@ private: int _overload_count; // counter for # of times a OVERLOAD_M request is sent timestamp_t _overload_timeout; // overload time - timestamp_t _underload_timeout; // underload time + timestamp_t _underload_timeout; // underload time timestamp_t _next_periodic; // last timestamp executing periodic task }; diff --git a/VSS/handler.js b/VSS/handler.js index 4b555e69..caf78f0e 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -8,8 +8,8 @@ require('../common'); // default port for connecting / creating VON nodes var _VON_port = 37700; -//var _VON_gateway = '127.0.0.1'; -var _VON_gateway = 'dev.imonology.com'; +var _VON_gateway = '127.0.0.1'; +//var _VON_gateway = 'dev.imonology.com'; // default radius when first joined // TODO: need to have default radius? @@ -147,11 +147,11 @@ var _reply = function (res, res_obj) { var _replySubscribers = function (request, res) { var node = _getNode(request, res); - - LOG.debug('replySubscribers called, node id: ' + node.getSelf().id); if (node !== undefined && node !== null) { - + + LOG.debug('replySubscribers called, node id: ' + node.getSelf().id); + // get a list of current neighbors's id var neighbors = node.list(); var list = []; diff --git a/common.js b/common.js index 4ef9027b..f3b15d52 100644 --- a/common.js +++ b/common.js @@ -1,14 +1,14 @@ /* - common definitions for VAST + common definitions for VAST.js */ // // utilities // -var logger = require('./common/logger'); -global.LOG = new logger(); +var logger = require('./common/logger'); +global.LOG = new logger(); global.UTIL = require('./common/util'); // set default error level @@ -18,11 +18,43 @@ LOG.setLevel(3); // VAST & VON // // ID definitions -global.VAST_ID_GATEWAY = 1; -global.VAST_ID_UNASSIGNED = 0; +global.VAST_ID_UNASSIGNED = 0; +global.VAST_ID_GATEWAY = 1; -global.VAST = require('./vast_types'); -global.VAST.net = require('./net/vast_net'); -global.VON = { +global.VAST = require('./vast_types'); +global.VAST.net = require('./net/vast_net'); +global.VAST.client = require('./VAST'); +global.VAST.matcher = require('./VAST_matcher'); + +// TODO: find a better way to store this? (maybe in msg_handler?) +global.VAST.state = { + ABSENT: 0, + INIT: 1, // init done + JOINING: 2, // different stages of join + JOINED: 3 +}; + +global.VAST.priority = { + HIGHEST: 0, + HIGH: 1, + NORMAL: 2, + LOW: 3, + LOWEST: 4 +}; + +// TODO: combine into nicer-looking global +global.VAST.msgtype = { + BYE: 0, // disconnect + PUB: 1, // publish request + SUB: 2 // subscribe request +}; + +global.VAST.msgstr = [ + 'BYE', + 'PUB', + 'SUB' +]; + +global.VON = { peer: require('./VON_peer') }; diff --git a/net/msg_handler.js b/net/msg_handler.js index 923dc135..482dc20b 100644 --- a/net/msg_handler.js +++ b/net/msg_handler.js @@ -34,50 +34,171 @@ supported functions: // basic callback / structure - sendMessage - sendPack - _msgHandler + addr = {host, port} + l_onDone(addr) // constructor + self_id id for self node + listen_port port to listen/bind locally + onDone callback to notify when binding is finished // basic functions + addHandler add a new handler to message handler + removeHandler remove an existing handler from message handler + sendMessage send a javascript object to a SINGLE target node (given message 'type' and 'priority') + sendPack send a pack object to its destinated targets + storeMapping store mapping between id and address + disconnect disconnect from a particular host id // accessors - + getAddress get locally detected & binded network address + getID get unique ID for the net layer + history: 2012-10-03 initial version + 2012-11-04 add addHandler/removeHandler methods */ -function msg_handler(l_connHandler, l_disconnHandler, l_packetHandler, l_self_id) { - - // prevent incorrect init - if (l_connHandler === undefined || - l_disconnHandler === undefined || - l_packetHandler === undefined) - return; - +function msg_handler(l_self_id, l_listen_port, l_onDone) { + // // public methods (usable by classes that inherent the msg_handler class) // + + // + // add a new handler to message handler, the handler should provide the following: + // connHandler handler for connection event + // disconnHandler handler for disconnection event + // packetHandler handler to process a packet, return 'true' if success + // + + this.addHandler = function (handler_class) { + + // first check if they're all valid + if (typeof handler_class.connHandler !== 'function') { + LOG.error('addHandler: new handler does not have connHandler'); + return false; + } + + if(typeof handler_class.disconnHandler !== 'function') { + LOG.error('addHandler: new handler does not have disconnHandler'); + return false; + } + + if(typeof handler_class.packetHandler !== 'function') { + LOG.error('addHandler: new handler does not have packetHandler'); + return false; + } + + if(typeof handler_class.initStates !== 'function') { + LOG.error('addHandler: new handler does not have initStates'); + return false; + } + + // store handlers + _handlers.push(handler_class); + + // store this msg_handler to the handler + handler_class.initStates(this); + return true; + } + + // remove an existing handler from message handler + this.removeHandler = function (handler) { + + for (var i=0; i < _handlers.length; i++) + // handler found, remove it + if (_handlers[i] == handler) { + _handlers.splice(i, 1); + return true; + } + + // no matching handler found + return false; + } + + // send a javascript object to a SINGLE target node (given message 'type' and 'priority') + this.sendMessage = function (target, msg_type, js_obj, priority, is_reliable) { + + // create a delivery package to send + var pack = new VAST.pack( + msg_type, + js_obj, + priority); + + pack.targets.push(target); + + //console.log('_net: ' + _net + ' target: ' + target); + + // convert pack to JSON or binary string + return _that.sendPack(pack, is_reliable); + } + + // send a pack object to its destinated targets + this.sendPack = function (pack, is_reliable) { + + // do nothing is target is empty + if (pack.targets.length === 0) + return; + + // serialize string + var encode_str = JSON.stringify(pack); + + // go through each target and send + // TODO: optimize so only one message is sent to each physical host target + var to_list = ''; + var self_id = _net.getID(); + + for (var i=0; i < pack.targets.length; i++) { + + _net.send(pack.targets[i], encode_str, is_reliable); + to_list += (pack.targets[i] + ','); + } + + LOG.debug('SEND to [' + to_list + ']: ' + encode_str); + } + + // store a network id to address mapping + this.storeMapping = function (id, addr) { + return _net.storeMapping(id, addr); + } + + // disconnect a remote host + this.disconnect = function (id) { + return _net.disconnect(id); + } + + // get locally detected & binded network address + this.getAddress = function () { + return _localAddress; + } + + // obtain self ID from network layer + this.getID = function () { + return _net.getID(); + } + // // private methods (internal usage, some are replacable at inherted class) // - - // constructor - //LOG.debug('msg_handler init, l_packetHandler: ' + typeof l_packetHandler); - - // NOTE: packet & message handlers cannot be of prototype-style, - // as they need to occupy memory independently for each msg handler instance + + var _net = undefined; // a reference to vast_net - // default packet handler (does not process anything) - //var _handlePacket = undefined; + // handlers registered to handle connect/disconnect/packet + var _handlers = []; + + // local IP & port + var _localAddress = {host: '127.0.0.1', port: 0}; // keep local reference for 'this' var _that = this; - + + // NOTE: packet & message handlers cannot be of prototype-style, + // as they need to occupy memory independently for each msg handler instance + // handler for incoming messages - this.msgHandler = function (from_id, msg) { + var _packetHandler = function (from_id, msg) { // prevent processing invalid msg // NOTE: we allow for empty message (?) such as VON_DISCONNECT @@ -86,6 +207,8 @@ function msg_handler(l_connHandler, l_disconnHandler, l_packetHandler, l_self_id return; } + LOG.debug('RECV from [' + from_id + ']: ' + msg); + var pack; try { @@ -96,80 +219,77 @@ function msg_handler(l_connHandler, l_disconnHandler, l_packetHandler, l_self_id LOG.error('msgHandler: convert to js_obj fail: ' + e + ' msg: ' + msg); return; } - - if (l_packetHandler === undefined) { - LOG.error('no valid packet handler for type: ' + pack.type + ' msg: ' + msg); - return; + + // go through each packet handler and see which will handle it + for (var i=0; i < _handlers.length; i++) { + if (typeof _handlers[i].packetHandler === 'function') + if (_handlers[i].packetHandler(from_id, pack) === true) { + // successfully handle incoming packet, return + return true; + } + } + + LOG.error('no packet handler for message type: ' + pack.type + ' msg: ' + msg); + return false; + } + + // handler for connection notification + var _connHandler = function (id) { + + // notify each registered handler for connection + for (var i=0; i < _handlers.length; i++) { + if (typeof _handlers[i].connHandler === 'function') + _handlers[i].connHandler(id); } + } - //LOG.debug('call packetHandler: ' + typeof l_packetHandler); - if (l_packetHandler(from_id, pack) === false) - LOG.error('cannot recongize message type: ' + pack.type + ' msg: ' + msg); - } + // handler for disconnection notification + var _disconnHandler = function (id) { - /* - // initialize message handler - this.init = function (connHandler, disconnHandler, packetHandler, self_id) { - - // replace default with provided custom handler, if any - if (typeof packetHandler === 'function') { - LOG.warn('handlePacket type is: ' + typeof _handlePacket); - _handlePacket = packetHandler; + // notify each registered handler for disconnection + for (var i=0; i < _handlers.length; i++) { + if (typeof _handlers[i].disconnHandler === 'function') + _handlers[i].disconnHandler(id); } - - LOG.debug('msg_handler:init() called. checking existence of net object...'); - - // create new net layer if does not exist - if (this.net === undefined) { - LOG.warn('creating new VASTnet...'); - - // create network layer & start listening - // NOTE: internal handlers must be defined before creating the VAST.net instance - this.net = new VAST.net(this.msgHandler, connHandler, disconnHandler, self_id); - } - } - */ - + } + + // + // constructor (record or create a new vast_net layer) + // + + // constructor + LOG.debug('msg_handler init, l_self_id: ' + l_self_id); + // create new net layer if does not exist - LOG.warn('creating new VASTnet... net: ' + typeof this.net); - + LOG.warn('creating new VASTnet... net: ' + typeof _net); + // create network layer & start listening // NOTE: internal handlers must be defined before creating the VAST.net instance - this.net = new VAST.net(this.msgHandler, l_connHandler, l_disconnHandler, l_self_id); - -} // end msg_handler - - -// send a pack object to its destinated targets -// TODO: this previously exist at the network layer, should move it there? -msg_handler.prototype.sendPack = function (pack, is_reliable) { - - // serialize string - var encode_str = JSON.stringify(pack); - - // go through each target and send - // TODO: optimize so only one message is sent to each physical host target - for (var i=0; i < pack.targets.length; i++) - this.net.send(pack.targets[i], encode_str, is_reliable); -} - -// send a javascript object to a SINGLE target node (given message 'type' and 'priority') -msg_handler.prototype.sendMessage = function (target, msg_type, js_obj, priority, is_reliable) { - - // create a delivery package to send - var pack = new VAST.pack( - msg_type, - js_obj, - priority); + _net = new VAST.net(_packetHandler, _connHandler, _disconnHandler, l_self_id); - pack.targets.push(target); + LOG.debug('calling getHost()'); - //console.log('this.net: ' + this.net + ' target: ' + target); + // create self object + _net.getHost(function (local_IP) { - // convert pack to JSON or binary string - // NOTE: we pass the same context - return msg_handler.prototype.sendPack.call(this, pack, is_reliable); -} + LOG.debug('local IP: ' + local_IP); + + // store my locally detected IP + _localAddress.host = local_IP; + + // return value is actual port binded + _net.listen(l_listen_port, function (actual_port) { + + // store actual port binded + _localAddress.port = actual_port; + + // notify port binding success + if (typeof l_onDone === 'function') + l_onDone(_localAddress); + }); + }); + +} // end msg_handler if (typeof module !== 'undefined') module.exports = msg_handler; diff --git a/net/vast_net.js b/net/vast_net.js index 1c0f6d3e..ab0cbc6b 100644 --- a/net/vast_net.js +++ b/net/vast_net.js @@ -451,7 +451,7 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // TODO: try to determine the cause & fix this // NOTE: if this happens, the message is simply ignored if (isNaN(remote_id)) { - LOG.error('[' + _self_id + '] remote_id is NaN, str: ' + str + ' from id: ' + socket.id); + LOG.error('[' + _self_id + '] remote_id is NaN, str: ' + str + ' from socket id: ' + socket.id); } else { @@ -486,6 +486,8 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // otherwise is a real msg, notify custom callback of incoming data (if provided) else if (typeof CB_receive === 'function') { //LOG.debug('recv from [' + socket.id + '] str: ' + str); + + // TODO: queue-up message to be processed later? CB_receive(socket.id, str); } } diff --git a/test_VAST_client.js b/test_VAST_client.js new file mode 100644 index 00000000..276a9bcc --- /dev/null +++ b/test_VAST_client.js @@ -0,0 +1,81 @@ +/* + unit test for VAST.js + + history: + 2012.11.08 init +*/ + + + +// flags +var AUTOMATIC_LEAVE_PERIOD = 3; // number of seconds + +require('./common'); + +// do not show debug +//LOG.setLevel(2); + +// set default IP/port +var ip_port = {host: "127.0.0.1", port: 37700}; + +// get custom parameters +var port = process.argv[2]; +if (port !== undefined) + ip_port.port = port; + +var host = process.argv[3]; +var is_client = false; +if (host !== undefined) { + ip_port.host = host; + is_client = true; +} + +LOG.debug('host: ' + ip_port.host + ' port: ' + ip_port.port + ' is_client: ' + is_client); + +var x = Math.floor(Math.random() * 100); +var y = Math.floor(Math.random() * 100); + +// create GW or a connecting client; +var client = new VAST.client(); +var aoi = new VAST.area(new VAST.pos(x, y), 100); + +var moveAround = function () { + + // move if not GW + if (client.getSelf().id !== VAST_ID_GATEWAY) { + // random walk new location (5 units within current center position) + aoi.center.x += Math.floor((Math.random()%10 - 5)); + aoi.center.y += Math.floor((Math.random()%10 - 5)); + } + + var neighbor_size = Object.keys(client.list()).length; + console.log('move around to ' + aoi.center.toString() + ' neighbor size: ' + neighbor_size); + client.move(aoi); +} + +// join to myself as the gateway +// NOTE: interesting idea to always be able to "join self" (i.e., everyone is a gateway) +var interval_id = undefined; + +// after init the client will bind to a local port +client.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), ip_port.port, function () { + + LOG.warn('test_VAST_client: init done'); + + client.join(ip_port, + + // done callback + function (id) { + LOG.warn('VAST client joined successfully! id: ' + id + '\n'); + + /* + // try to move around once in a while... (if not gateway) + if (id !== VAST_ID_GATEWAY) { + interval_id = setInterval(function(){ moveAround() }, 1000); + } + */ + } + ); + +}); + diff --git a/test_VON_peer.js b/test_VON_peer.js index 7cb550a8..3e2cdf97 100644 --- a/test_VON_peer.js +++ b/test_VON_peer.js @@ -19,29 +19,39 @@ require('./common'); //LOG.debug(curr.toLocaleString()); // do not show debug -//LOG.setLevel(2); +LOG.setLevel(2); // set default IP/port -var ip_port = {host: "127.0.0.1", port: 37700}; - -var port = process.argv[2]; -if (port !== undefined) - ip_port.port = port; - -var host = process.argv[3]; +var gateway_addr = {host: "127.0.0.1", port: 37700}; var is_client = false; -if (host !== undefined) { - ip_port.host = host; - is_client = true; + +// IP/port +if (process.argv[2] !== undefined) { + var ip_port = process.argv[2]; + // check if this is port only + var idx = ip_port.search(':'); + + // ':' not found, port only + if (idx === (-1)) + gateway_addr.port = parseInt(ip_port); + else { + var ip = ip_port.slice(0, idx); + var port = ip_port.slice(idx+1, ip_port.length); + gateway_addr.host = ip; + gateway_addr.port = parseInt(port); + + is_client = true; + } } -LOG.debug('host: ' + ip_port.host + ' port: ' + ip_port.port + ' is_client: ' + is_client); +LOG.debug('GW ip: ' + gateway_addr.host + ' port: ' + gateway_addr.port); +LOG.debug('is_client: ' + is_client); var x = Math.floor(Math.random() * 100); var y = Math.floor(Math.random() * 100); // create GW or a connecting client; -var peer = new VON.peer((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), ip_port.port); +var peer = new VON.peer(); var aoi = new VAST.area(new VAST.pos(x, y), 100); var moveAround = function () { @@ -62,15 +72,20 @@ var moveAround = function () { // NOTE: interesting idea to always be able to "join self" (i.e., everyone is a gateway) var interval_id = undefined; -peer.join(ip_port, aoi, +// after init the peer will bind to a local port +peer.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.port, gateway_addr, function () { + + peer.join(aoi, + + // done callback + function (id) { + LOG.warn('joined successfully! id: ' + id + '\n'); + + // try to move around once in a while... (if not gateway) + if (id !== VAST_ID_GATEWAY) { + interval_id = setInterval(function(){ moveAround() }, 1000); + } + } + ); +}); - // done callback - function (id) { - LOG.warn('joined successfully! id: ' + id + '\n'); - - // try to move around once in a while... (if not gateway) - if (id !== VAST_ID_GATEWAY) { - interval_id = setInterval(function(){ moveAround() }, 1000); - } - } -); diff --git a/test_VON_scale.js b/test_VON_scale.js index 8f39e930..0cedcce8 100644 --- a/test_VON_scale.js +++ b/test_VON_scale.js @@ -61,7 +61,7 @@ var VONnode = function (num, GWaddr, radius) { var pos = movement.getpos(num-1); // create GW or a connecting client; - var peer = new VON.peer(VAST_ID_UNASSIGNED, port); + var peer = new VON.peer(); var aoi = new VAST.area(new VAST.pos(pos.x, pos.y), radius); // perform movement @@ -75,16 +75,19 @@ var VONnode = function (num, GWaddr, radius) { peer.move(aoi); } - peer.join(GWaddr, aoi, - - // done callback - function (id) { - LOG.warn('joined successfully! id: ' + id + '\n'); - - if (id !== VAST_ID_GATEWAY) - setInterval(moveNode, tick_interval); - } - ); + peer.init(VAST_ID_UNASSIGNED, port, GWaddr, function () { + + peer.join(aoi, + + // done callback + function (id) { + LOG.warn('joined successfully! id: ' + id + '\n'); + + if (id !== VAST_ID_GATEWAY) + setInterval(moveNode, tick_interval); + } + ); + }); } var nodes_created = 0; diff --git a/vast.io/vast.io-server.js b/vast.io/vast.io-server.js index 6b6e4361..3e8fb577 100644 --- a/vast.io/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -8,7 +8,8 @@ process.on('uncaughtException', function(err) { // include VAST require('../common'); -//LOG.setLevel(2); +// show only warnings +LOG.setLevel(2); /* NOTE: @@ -79,23 +80,25 @@ io.sockets.on('connection', function (socket) { // create GW or a connecting client // NOTE: by using VAST_ID_UNASSIGNED as default, the first created node will be the gateway - _self = new VON.peer(VAST_ID_UNASSIGNED, ip_port.port + nodes_created); + _self = new VON.peer(); nodes_created++; // join in the network - _self.join(ip_port, aoi, - - // done callback - function (self_id) { - - LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + _self.getSelf().id); - - // reply join success & ID - socket.emit('join_r', {id: self_id}); - } - ); - + _self.init(VAST_ID_UNASSIGNED, ip_port.port + nodes_created, ip_port, function () { + + _self.join(aoi, + + // done callback + function (self_id) { + + LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + _self.getSelf().id); + + // reply join success & ID + socket.emit('join_r', {id: self_id}); + } + ); + }); }); // leave VON network diff --git a/vast_types.js b/vast_types.js index 2c3cd6ea..9268323f 100644 --- a/vast_types.js +++ b/vast_types.js @@ -103,7 +103,7 @@ var l_area = exports.area = function (center, radius) { } this.toString = function () { - return 'Area: ' + this.center.toString() + ' radius: ' + this.radius; + return 'area: ' + this.center.toString() + ' radius: ' + this.radius; } // update info from existing object @@ -136,15 +136,10 @@ var l_area = exports.area = function (center, radius) { // definition for a connectable IP/port pair (vast_addr) var l_addr = exports.addr = function (host, port) { - - // set default - if (host === undefined) - host = 0; - if (port === undefined) - port = 0; - - this.host = host; - this.port = port; + + // set initial value or default + this.host = host || 0; + this.port = port || 0; // turn object to string representation this.toString = function () { @@ -221,20 +216,13 @@ var l_endpt = exports.endpt = function (host, port) { // 'aoi': a vast_area object // 'endpt': a vast_endpt object var l_node = exports.node = function (id, endpt, aoi, time) { - - // set default - if (endpt === undefined) - endpt = new l_endpt(); - if (aoi === undefined) - aoi = new l_area(); - if (time === undefined) - time = 0; - - this.id = id; // a node's unique ID - this.endpt = endpt; // a node's contact endpoint (host_id & address) - this.aoi = aoi; // a node's AOI (center + radius) - this.time = time; // a node's last updated time (used to determine whether it contains newer info) - //this.meta = {}; // a node's meta-data, default to empty + + // set initial values or default + this.id = id || 0; // a node's unique ID + this.endpt = endpt || new l_endpt(); // a node's contact endpoint (host_id & address) + this.aoi = aoi || new l_area(); // a node's AOI (center + radius) + this.time = time || 0; // a node's last updated time (used to determine whether it contains newer info) + //this.meta = {}; // a node's meta-data, default to empty // update node info from another node // NOTE: this is powerful as it replaces almost everything (perhaps ID shouldn't be replaced?) @@ -293,10 +281,7 @@ var l_pack = exports.pack = function (type, msg, priority) { // the message content this.msg = msg; - - // group is used to determine who will handle this message (still needed?) - this.group = 0; - + // default priority is 1 this.priority = (priority === undefined ? 1 : priority); @@ -314,4 +299,51 @@ var l_ratio = exports.ratio = function () { this.ratio = function () { return normal / total; } -} \ No newline at end of file +} + +// definition for a node +// 'aoi': a vast_area object +// 'endpt': a vast_endpt object +var l_sub = exports.sub = function (host_id, id, layer, aoi) { + + // set default + host_id = host_id || 0; + id = id || 0; + layer = layer || 1; + aoi = aoi || new l_area(); + + this.host_id = host_id; // 'number' HostID of the subscriber + this.id = id; // 'number' subscriptionID (different subscriptions may have same hostID) + this.layer = layer; // 'number' layer number for the subscription + this.aoi = aoi; // 'area' aoi of the subscription (including a center position) + //this.relay = relay; // 'endpt' the address of the relay of the subscriber (to receive messages) + + + // update info from existing record + // NOTE: we only replace if data is available + this.update = function (info) { + + this.host_id = (info.host_id !== 0 ? info.host_id : this.host_id); + this.id = (info.id !== 0 ? info.id : this.id); + this.layer = (info.layer !== 0 ? info.layer : this.layer); + this.aoi.update(info.aoi); + } + + // parse a json object onto a node object + this.parse = function (js_obj) { + try { + this.host_id = parseInt(js_obj.host_id); + this.id = parseInt(js_obj.id); + this.layer = parseInt(js_obj.layer); + this.aoi.parse(js_obj.aoi); + } + catch (e) { + console.log('sub parse error: ' + e); + } + } + + // print out node info + this.toString = function () { + return '[' + this.id + '] host_id: ' + host_id + ' layer: ' + this.layer + ' AOI: ' + this.aoi.toString(); + } +} From 70c68074574e948b44b2cb57ccfee0f22dd07c55 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 22 Nov 2012 19:07:43 +0800 Subject: [PATCH 016/141] unsubscribe for VSS will not report itself when positions overlapped --- VAST WorkLog.txt | 5 +++ VON_peer.js | 27 ++++++++------ VSS/VSS.js | 3 ++ VSS/handler.js | 96 +++++++++++++++++++++++++----------------------- common/logger.js | 9 +++++ vast_voro.js | 10 +++-- 6 files changed, 90 insertions(+), 60 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index e19150de..8aca575b 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -61,10 +61,15 @@ goal: make VON_peer to work with VON_PING to obtain initial ID - add support of VON_PING and modify join procedure - modify relevant programs that use VON_peer for new interface (vast.io-server, test_VON_peer, test_VON_scale) +- modify behavior: if a node unsubscribe (with vast.io) it will not be listed as subscriber even when node positions + overlap (previously it simply set subscription radius to 0). + +KNOWN ISSUES: - seems working for up to 50 nodes via test_VON_scale. but there are many "got VON_HELLO_R from unknown neighbor [xx]" warnings also many remote id is NaN & negative ID are received... many are VON_HELLO, VON_EN, VON_MOVE & VON_MOVE_B +- continous debug msg being printed when nodes have overlapped position 2012-11-21 (3) diff --git a/VON_peer.js b/VON_peer.js index 63185fdb..f124a5a6 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -36,18 +36,18 @@ pack = {type, msg, group, priority, targets} // message package during delivery vast_net (see definition in vast_net.js) - // constructor - VON_peer(aoi_buffer, aoi_use_strict) - - // basic functions - init(id, port, addr, done_CB) init a VON peer with id, listen port & gateway's address - query(center, acceptor_CB) find the acceptor for a given center point + // constructoracceptor for a given center point join(aoi, done_CB) join a VON network with a given aoi leave() leave the VON network move(aoi, send_time) move the AOI to a new position (or change radius) list() get a list of AOI neighbors send(id, msg) send a message to a given node - put(obj) store a app-specific data along with the node (will pass during node discovery) + put(obj) sto + VON_peer(aoi_buffer, aoi_use_strict) + + // basic functions + init(id, port, addr, done_CB) init a VON peer with id, listen port & gateway's address + query(center, acceptor_CB) find the re a app-specific data along with the node (will pass during node discovery) get() retrieve app-specific data for this node // accessors @@ -592,7 +592,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _removeNonOverlapped(); } catch (e) { - LOG.error('tick error: ' + e); + LOG.error('tick error:\n' + e.stack); } } @@ -1106,7 +1106,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // TODO: more efficient check (EN of joiner is calculated multiple times now) var is_relevant = _isRelevantNeighbor(neighbor, joiner); - //LOG.debug('is relevant: ' + is_relevant); + LOG.debug('is relevant: ' + is_relevant); if (neighbor.id != joiner.id && is_relevant && _isTimelyNeighbor(neighbor.id)) { @@ -1234,8 +1234,13 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { neighbor.meta = pack.msg.meta; } - LOG.debug('got latest pos: ' + neighbor.aoi.center.toString() + ' id: ' + from_id); - _updateNode(neighbor); + LOG.debug('got latest pos: ' + neighbor.aoi.center.toString() + ' id: ' + from_id); + try { + _updateNode(neighbor); + } + catch(e) { + LOG.debug(e.stack); + } } else LOG.warn('[' + _self.id + '] got VON_HELLO_R from unknown neighbor [' + from_id + ']'); diff --git a/VSS/VSS.js b/VSS/VSS.js index 510efffe..caef2964 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -2,6 +2,9 @@ // include VAST require('../common'); +// do not show debug +LOG.setLevel(2); + var server = require("./server"); var router = require("./router"); var handlers = require("./handler"); diff --git a/VSS/handler.js b/VSS/handler.js index caf78f0e..cf502bfb 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -49,53 +49,56 @@ var _registerNode = function (pos, info, done_CB) { // specify where to locate VON gateway var ip_port = new VAST.addr(_VON_gateway, _VON_port); - var new_node = new VON.peer(VAST_ID_UNASSIGNED, ip_port.port + _nodes_created); - _nodes_created++; - - LOG.debug('new_node (before join): ' + new_node.getSelf().toString()); + var new_node = new VON.peer(); + + // join in the network + new_node.init(VAST_ID_UNASSIGNED, ip_port.port + _nodes_created, ip_port, function () { - // store node ident for ident discovery across different VSS servers - var ident_info = { - apikey: info.apikey, - layer: info.layer, - ident: info.ident - }; + _nodes_created++; + LOG.debug('new_node (before join): ' + new_node.getSelf().toString()); + + // store node ident for ident discovery across different VSS servers + var ident_info = { + apikey: info.apikey, + layer: info.layer, + ident: info.ident + }; + + new_node.put(ident_info); + + new_node.join(aoi, + + // done callback + function (self_id) { + + LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + new_node.getSelf().id); + + // keep track of newly joined node in internal record + if (_keys.hasOwnProperty(info.apikey) === false) + _keys[info.apikey] = {}; + if (_keys[info.apikey].hasOwnProperty(info.layer) === false) + _keys[info.apikey][info.layer] = {}; + + _keys[info.apikey][info.layer][info.ident] = new_node; - new_node.put(ident_info); - - // join in the network - new_node.join(ip_port, aoi, + // store id to ident mapping + LOG.debug('store mapping for node id: ' + self_id + ' ident: ' + info.ident); + _id2ident[self_id] = info; + + //new_node.put(info); + + // check content + LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); - // done callback - function (self_id) { - - LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + new_node.getSelf().id); - - // keep track of newly joined node in internal record - if (_keys.hasOwnProperty(info.apikey) === false) - _keys[info.apikey] = {}; - if (_keys[info.apikey].hasOwnProperty(info.layer) === false) - _keys[info.apikey][info.layer] = {}; + // perform initial publish pos + // (so we can get a list of neighbors) + // NOTE: doesn't appear to work... + //_publishPos(new_node, info, new_node.getSelf().aoi.radius); - _keys[info.apikey][info.layer][info.ident] = new_node; - - // store id to ident mapping - LOG.debug('store mapping for node id: ' + self_id + ' ident: ' + info.ident); - _id2ident[self_id] = info; - - //new_node.put(info); - - // check content - LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); - - // perform initial publish pos - // (so we can get a list of neighbors) - // NOTE: doesn't appear to work... - //_publishPos(new_node, info, new_node.getSelf().aoi.radius); - - done_CB(new_node); - } - ); + done_CB(new_node); + } + ); + }); } var _revokeNode = function (info, done_CB) { @@ -168,8 +171,8 @@ var _replySubscribers = function (request, res) { var neighbor = neighbors[id]; - for (var i in neighbor) - LOG.debug(i + ': ' + typeof neighbor[i]); + //for (var i in neighbor) + // LOG.debug(i + ': ' + typeof neighbor[i]); // get node ident (from either 'meta' field or from mapping) var info = undefined; @@ -240,7 +243,8 @@ var _getNode = function (req, res) { // check if a given node is a direct area subscriber for a center point var _isSubscriber = function (node, aoi) { - var result = node.aoi.covers(aoi.center); + // NOTE: if subscription radius is 0, then we consider it's not subscribing anything + var result = (node.aoi.radius != 0 && node.aoi.covers(aoi.center)); LOG.debug('isSubscriber check if node ' + node.toString() + ' covers ' + aoi.center.toString() + ': ' + result); return result; } diff --git a/common/logger.js b/common/logger.js index 99ad35b5..e1e03745 100644 --- a/common/logger.js +++ b/common/logger.js @@ -46,6 +46,15 @@ function logger() { if (_level >= 1) console.log(_ERR + msg + _ERREND); } + + this.stack = function () { + var e = new Error('dummy'); + var stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '') + .replace(/^\s+at\s+/gm, '') + .replace(/^Object.\s*\(/gm, '{anonymous}()@') + .split('\n'); + console.log(stack); + } } if (typeof module !== 'undefined') diff --git a/vast_voro.js b/vast_voro.js index 508df501..d6ed14d9 100644 --- a/vast_voro.js +++ b/vast_voro.js @@ -197,7 +197,7 @@ function VAST_Voronoi(bbox) { // check if the node 'id' is an enclosing neighbor of 'center_node_id' this.is_enclosing = function (id, center_node_id) { - + // get index for the input node id var idx = check_id(center_node_id); if (idx == -1) @@ -501,13 +501,17 @@ function VAST_Voronoi(bbox) { // we thus print 'debug' instead of 'error' here if (id2idx.hasOwnProperty(id) === false) { if (isNaN(id)) - LOG.error('get_idx () cannot find index for id: ' + id); + LOG.error('get_idx () cannot find index for id: ' + id + ' (not a number)'); else LOG.debug('get_idx () cannot find index for id: ' + id); // check if index list and site list have equal length - if (Object.keys(id2idx).length !== Object.keys(sites).length) + if (Object.keys(id2idx).length !== Object.keys(sites).length) { LOG.debug('list size mismatch! there may be overlapped site coords. id2idx size: ' + Object.keys(id2idx).length + ' sites size: ' + Object.keys(sites).length); + //throw new Error("list size mismatch, potential overlapped site coords"); + + //LOG.stack(); + } return (-1); } From 0157f291196cd53c89a49d4d63aab9cde6796fb4 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 27 Nov 2012 17:39:30 +0800 Subject: [PATCH 017/141] add close() to net layers and shut() to VON_peer, VAST.js works with VON_peer initally --- VAST WorkLog.txt | 36 +++++++++++++++++++++ VAST.js | 78 ++++++++++++++++++++++++++-------------------- VAST_matcher.js | 11 +++++-- VON_peer.js | 16 ++++++++++ VSS/handler.js | 2 ++ net/msg_handler.js | 6 ++++ net/net_nodejs.js | 26 ++++++++++++++-- net/vast_net.js | 31 ++++++++++++------ test_VON_peer.js | 7 +---- test_VON_scale.js | 6 ---- vast_types.js | 12 ++++--- 11 files changed, 166 insertions(+), 65 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 8aca575b..ef53d494 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -52,6 +52,42 @@ Overall Goals: implement generic SPS based on matcher-client design simplify msg_handler usage (handler needs to implement many things...) +2012-11-27 (1) +------------- +goal: fix port does not close after revokeNode is called in VSS + + + +2012-11-26 (1) +------------- +goal: implement subscribe() in VAST.js (matcher-side) + +- stuck at how to initialize vast_matcher (need to figure out: how to initialize a matcher) +- added 'close()' function in net_nodejs & vast_net to close up a listening server + + +2012-11-25 (7) +------------- +goal: implement subscribe() in VAST.js (matcher-side) + +- re-check basic client-side implementation +- decide that 'host_id' in subscription info is still important (to forward matching publications) + + +2012-11-24 (6) +------------- +goal: implement subscribe() in VAST.js (matcher-side) + +- consider to remove host_id in 'endpt' and 'sub' data structures + + +2012-11-23 (5) +------------- +goal: implement subscribe() in VAST.js (matcher-side) + +- need to consider two issues: + whether host_id is necessary in a subscription + whether each vast node can do multiple subscriptions (or limited to single) 2012-11-22 (4) diff --git a/VAST.js b/VAST.js index 42d274cb..b26c5441 100644 --- a/VAST.js +++ b/VAST.js @@ -93,8 +93,11 @@ function VAST_client(recv_callback, settings, GW_addr) { // my subscription record var _sub = VAST.sub(); + // callback to notify when subscribe is done + var _sub_CB = undefined; + // information regarding current node - var _self; + var _self; // id for owner matcher, default to gateway var _matcher_id = VAST_ID_GATEWAY; @@ -128,33 +131,35 @@ function VAST_client(recv_callback, settings, GW_addr) { // // public methods // - - var _subscribeRetry = function () { - - } - + // subscribe for an area at a given layer this.subscribe = function (layer, area, sub_CB) { - /* - // set timeout to re-try, necessary because it takes time to send the subscription - _timeout_subscribe = _net->getTimestamp () + (TIMEOUT_SUBSCRIBE * _net->getTimestampPerSecond ()); - - // keep trying each TIMEOUT_SUBSCRIBE seconds - setTimeout(_subscribeRetry, 1000 * TIMEOUT_SUBSCRIBE); + // record callback when subscribe is done + _sub_CB = sub_CB; + + // store host to which matching publication should be sent (back to myself) + _sub.host_id = _getID(); + //_sub.host_id = _net->getHostID (); + //_sub.active = false; + //_sub.relay = _net->getAddress (_relay->getRelayID ()); + // record my subscription, not yet successfully subscribed - // NOTE: current we assume we subscribe only one at a time + // NOTE: currently we assume we subscribe only one at a time // also, it's important to record the area & layer first, so that // re-subscribe attempt may be correct (in case the check below is not passed yet) - + // NOTE: because my hostID is unique, it guarantee my subscription ID is also unique if (_sub.id === VAST_ID_UNASSIGNED) _sub.id = _getID(); _sub.aoi = area; _sub.layer = layer; - */ + + // activate re-try mechanism + // NOTE: is this such a good idea (will result in request flooding?) + _subscribeRetry(); /* // if matcher or relay is not yet known, wait first @@ -164,21 +169,7 @@ function VAST_client(recv_callback, settings, GW_addr) { return false; } */ - - /* - // TODO: check if this is correct... do we need to separate host_id & sub_id ? - _sub.host_id = _getID(); - //_sub.host_id = _net->getHostID (); - //_sub.active = false; - //_sub.relay = _net->getAddress (_relay->getRelayID ()); - - LOG.debug('[' + _self.id + '] sends SUBSCRIBE request to [' + _matcher_id + ']'); - - // send out subscription request to owner matcher - var pack = new VAST.pack(VAST.msgtype.SUB, _sub, VAST.priority.HIGH); - return sendMatcherMessage(pack); - */ } // publish a message to an area at a given layer @@ -202,16 +193,34 @@ function VAST_client(recv_callback, settings, GW_addr) { // private methods // + var _subscribeRetry = function () { + + // no need to re-try if subscription is completed + if (_sub_CB === undefined) + return; + + LOG.debug('[' + _self.id + '] sends SUBSCRIBE request to [' + _matcher_id + ']'); + + // set timeout to re-try, necessary because it takes time to send the subscription, which can be lost + setTimeout(_subscribeRetry, 1000 * TIMEOUT_SUBSCRIBE); + + // send out subscription request to owner matcher + var pack = new VAST.pack(VAST.msgtype.SUB, _sub, VAST.priority.HIGH); + _sendMatcherMessage(pack); + } + // send to gateway a message on a given handler var _sendGatewayMessage = function (pack) { + pack.targets = []; pack.targets.push(VAST_ID_GATEWAY); _sendPack(pack, true); } // send to gateway a message on a given handler var _sendMatcherMessage = function (pack) { - + + pack.targets = []; pack.targets.push(_matcher_id); _sendPack(pack, true); } @@ -273,13 +282,14 @@ function VAST_client(recv_callback, settings, GW_addr) { // function to create a new net layer this.init = function (self_id, port, done_CB) { + self_id = self_id || VAST_ID_UNASSIGNED; port = port || VAST_DEFAULT_PORT; - // create new layer - var m_handler = new msg_handler(self_id, port, function (local_addr) { + // create message handler manager and add self as one of the handlers + var handler = new msg_handler(self_id, port, function (local_addr) { // NOTE: this will cause initStates() be called - m_handler.addHandler(_that); + handler.addHandler(_that); // notify done if (typeof done_CB === 'function') @@ -342,4 +352,4 @@ function VAST_client(recv_callback, settings, GW_addr) { // export the class with conditional check if (typeof module !== "undefined") - module.exports = VAST_client; + module.exports = VAST_client; diff --git a/VAST_matcher.js b/VAST_matcher.js index 5440338c..e1cffadb 100644 --- a/VAST_matcher.js +++ b/VAST_matcher.js @@ -80,6 +80,7 @@ function VAST_matcher(recv_callback, settings) { // send to gateway a message on a given handler var _sendGatewayMessage = function (pack) { + pack.targets = []; pack.targets.push(VAST_ID_GATEWAY); _sendPack(pack, true); } @@ -342,13 +343,14 @@ function VAST_matcher(recv_callback, settings) { // function to create a new net layer this.init = function (self_id, port, done_CB) { + self_id = self_id || VAST_ID_UNASSIGNED; port = port || VAST_DEFAULT_PORT; // create new layer - var m_handler = new msg_handler(self_id, port, function (local_addr) { + var handler = new msg_handler(self_id, port, function (local_addr) { // NOTE: this will cause initStates() be called - m_handler.addHandler(_that); + handler.addHandler(_that); // notify done if (typeof done_CB === 'function') @@ -374,6 +376,11 @@ function VAST_matcher(recv_callback, settings) { // add VON peer as self node _self = new VON.peer(); + + // provide self id, self port, gateway address, and callback when done + // TODO: how to init a matcher? + //_self.init(id, + _msg_handler.addHandler(_self); } } diff --git a/VON_peer.js b/VON_peer.js index f124a5a6..8c3afd4d 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -47,6 +47,7 @@ // basic functions init(id, port, addr, done_CB) init a VON peer with id, listen port & gateway's address + shut() shutdown a VON peer (will close down listen port) query(center, acceptor_CB) find the re a app-specific data along with the node (will pass during node discovery) get() retrieve app-specific data for this node @@ -176,6 +177,21 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { }); } + // shutdown a VON peer (will close down listen port) + this.shut = function () { + + if (_msg_handler !== undefined) { + // stop server, if currently listening + // NOTE: if VON peer is used with other nodes, stopping server here will impact other nodes as well + _msg_handler.close(); + _msg_handler = undefined; + } + + _self = undefined; + + _state = VAST.state.ABSENT; + } + // find the acceptor for a given center point var _query = this.query = function (contact_id, center, msg_type, msg_para) { diff --git a/VSS/handler.js b/VSS/handler.js index cf502bfb..e268bf8c 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -113,7 +113,9 @@ var _revokeNode = function (info, done_CB) { //destroy node node.leave(); + node.shut(); node = null; + delete _keys[info.apikey][info.layer][info.ident]; // check if we need to remove layer and/or API key diff --git a/net/msg_handler.js b/net/msg_handler.js index 482dc20b..ee356c5e 100644 --- a/net/msg_handler.js +++ b/net/msg_handler.js @@ -49,6 +49,7 @@ sendPack send a pack object to its destinated targets storeMapping store mapping between id and address disconnect disconnect from a particular host id + close stop the underlying network layer // accessors getAddress get locally detected & binded network address @@ -168,6 +169,11 @@ function msg_handler(l_self_id, l_listen_port, l_onDone) { this.disconnect = function (id) { return _net.disconnect(id); } + + // stop the underlying network layer + this.close = function () { + return _net.close(); + } // get locally detected & binded network address this.getAddress = function () { diff --git a/net/net_nodejs.js b/net/net_nodejs.js index 41790567..e35d308f 100644 --- a/net/net_nodejs.js +++ b/net/net_nodejs.js @@ -63,6 +63,9 @@ // return port binded, or 0 to indicate error listen(port, onDone) + // close the current listening server + close() + // check if I'm a listening server isServer() @@ -187,10 +190,12 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // pass back error for further processing _server.on('error', function (e) { - //LOG.error('net_nodejs: listen error caught: ' + e); + LOG.error('net_nodejs: listen error caught: ' + e.stack); //if (typeof onError === 'function') // onError(e); + _server = undefined; + if (typeof onDone === 'function') onDone(0); }); @@ -203,12 +208,29 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { }); } catch (e) { - LOG.error('net_nodejs: listen error crashed: ' + e); + LOG.error('net_nodejs: listen error crashed: ' + e.stack); if (typeof onError === 'function') onError('listen'); } } + // close the current listening server + this.close = function () { + try { + if (_server === undefined) { + LOG.error('net_nodejs: server not started'); + return false; + } + _server.close(); + _server = undefined; + return true; + } + catch (e) { + LOG.error('net_nodejs: server close error: ' + e.stack); + return false; + } + } + // check if this network socket is a listening server this.isServer = function () { return (_server !== undefined); diff --git a/net/vast_net.js b/net/vast_net.js index ab0cbc6b..cd7dce6d 100644 --- a/net/vast_net.js +++ b/net/vast_net.js @@ -25,20 +25,21 @@ supported functions: // basic callback / structure - addr = {host, port}; - CB_receive(id, msg); - CB_connect(id); - CB_disconnect(id); + addr = {host, port} + CB_receive(id, msg) + CB_connect(id) + CB_disconnect(id) // constructor vast_net(CB_receive, CB_connect, CB_disconnect); // basic functions - storeMapping(id, addr); store mapping from id to a particular host IP/port - setID(new_id, old_id); set self ID or change the id -> socket mapping for incoming connections - getID(); get self ID (which may be assigned by remote host) - send(id, msg, is_reliable); send a message to a target 'id' - listen(port, CB_done); start a server at a given 'port', port binded is returned via 'CB_done', 0 indicates error + storeMapping(id, addr) store mapping from id to a particular host IP/port + setID(new_id, old_id) set self ID or change the id -> socket mapping for incoming connections + getID() get self ID (which may be assigned by remote host) + send(id, msg, is_reliable) send a message to a target 'id' + listen(port, CB_done) start a server at a given 'port', port binded is returned via 'CB_done', 0 indicates error + close() close a server disconnect(id) disconnect connection to a remote node // socket related @@ -364,7 +365,17 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { _server.listen(port, listen_handler); } - + + // close a server + this.close = function () { + if (_server === undefined) + LOG.error('vast_net: server not started, cannot close'); + else { + _server.close(); + _server = undefined; + } + } + // remove an existing connection this.disconnect = function (id) { diff --git a/test_VON_peer.js b/test_VON_peer.js index 3e2cdf97..e6c26a24 100644 --- a/test_VON_peer.js +++ b/test_VON_peer.js @@ -4,11 +4,6 @@ 2012.07.10 */ -/* -process.on('uncaughtException', function(err) { - console.log(err); -}); -*/ // flags var AUTOMATIC_LEAVE_PERIOD = 3; // number of seconds @@ -19,7 +14,7 @@ require('./common'); //LOG.debug(curr.toLocaleString()); // do not show debug -LOG.setLevel(2); +//LOG.setLevel(2); // set default IP/port var gateway_addr = {host: "127.0.0.1", port: 37700}; diff --git a/test_VON_scale.js b/test_VON_scale.js index 0cedcce8..c5e6a01d 100644 --- a/test_VON_scale.js +++ b/test_VON_scale.js @@ -4,12 +4,6 @@ 2012.08.28 init */ -/* -process.on('uncaughtException', function(err) { - console.log(err); -}); -*/ - // flags require('./common'); var cluster_model = require('./move_cluster'); diff --git a/vast_types.js b/vast_types.js index 9268323f..8657f862 100644 --- a/vast_types.js +++ b/vast_types.js @@ -301,9 +301,12 @@ var l_ratio = exports.ratio = function () { } } -// definition for a node -// 'aoi': a vast_area object -// 'endpt': a vast_endpt object +// definition for a subscription +// 'host_id': to which host a matched publication should be sent +// 'id': unique subscription id +// 'layer': which layer the subscription belongs +// 'aoi': the aoi of the subscription + var l_sub = exports.sub = function (host_id, id, layer, aoi) { // set default @@ -317,8 +320,7 @@ var l_sub = exports.sub = function (host_id, id, layer, aoi) { this.layer = layer; // 'number' layer number for the subscription this.aoi = aoi; // 'area' aoi of the subscription (including a center position) //this.relay = relay; // 'endpt' the address of the relay of the subscriber (to receive messages) - - + // update info from existing record // NOTE: we only replace if data is available this.update = function (info) { From 731d104add4a442f10d4f9ae2c814343f6822c8f Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 27 Nov 2012 17:53:18 +0800 Subject: [PATCH 018/141] modify listen error display to single line --- VAST WorkLog.txt | 2 +- net/net_nodejs.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index ef53d494..14d1482c 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -1,4 +1,4 @@ - + To-Do: - add VON_REQUEST_ID msg or send VON_HELLO first? (for ID assignment without net layer involvement) diff --git a/net/net_nodejs.js b/net/net_nodejs.js index e35d308f..7f1daa70 100644 --- a/net/net_nodejs.js +++ b/net/net_nodejs.js @@ -190,7 +190,9 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // pass back error for further processing _server.on('error', function (e) { - LOG.error('net_nodejs: listen error caught: ' + e.stack); + + // NOTE: we do not display this as binding to a already binded port will cause this error + LOG.error('net_nodejs: listen error caught: ' + e); //if (typeof onError === 'function') // onError(e); From 9820dd220f95c6545792d292b927853d7eab1b08 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 5 Mar 2013 11:52:17 +0800 Subject: [PATCH 019/141] id attached to all vast_net messages --- INSTALL.txt | 9 + VAST WorkLog.txt | 397 +++++++++++++++++++++++++++- VAST.js | 5 +- VAST_matcher.js | 13 +- VON_peer.js | 307 ++++++++++++---------- VSS/VSS.js | 2 +- VSS/handler.js | 9 +- common.js | 2 +- common/logger.js | 26 +- net/msg_handler.js | 62 ++--- net/net_nodejs.js | 127 ++++----- net/vast_net.js | 500 ++++++++++++++++++++---------------- test_VAST_client.js | 38 ++- test_VON_peer.js | 19 +- test_VON_scale.js | 13 +- vast.io/vast.io-client.html | 4 +- vast.io/vast.io-server.js | 4 +- vast_types.js | 21 +- 18 files changed, 998 insertions(+), 560 deletions(-) create mode 100644 INSTALL.txt diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 00000000..8ac15e42 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,9 @@ + +Dependency +========== + +to run vast.io, socket.io is required to reside under the +vast.io directory. Install by go into the vast.io directory +and type: + +npm install socket.io \ No newline at end of file diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt index 14d1482c..92e280ec 100644 --- a/VAST WorkLog.txt +++ b/VAST WorkLog.txt @@ -19,9 +19,9 @@ BUG (*) denotes design bug: 2012-06-23: 1st & 2nd level enclosing neighbors are the same 2012-07-23: voro.overlaps returns 'false' always when there's only one region for any point 2012-08-29: scaling many nodes will cause TCP connection errors -2012-09-03: EADDRINUSE crash bug (after running a while with >50 nodes) 2012-09-24: send_msg in vast_net does not necessarily send out in FIFO order - +2012-11-27: shutdown 50 nodes at once will leave ghost nodes +2012-12-01: if node positions overlap, VSS server will show continouing warning messages ---- 2009-06-12 UDP transmission causes connection lost in real network @@ -51,12 +51,385 @@ BUG (*) denotes design bug: Overall Goals: implement generic SPS based on matcher-client design simplify msg_handler usage (handler needs to implement many things...) +debug ECONNABORTED (possibly over-connection), EFAULT + +2013-03-05 (2) +------------- +goal: identify ENOBUFS causes + +- found that there's a lot of redundent socket connections + + + +2013-03-04 (1) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + debug ENOBUFS + +- ENOBUFS is caused by lack of memory in server buffer + + +2013-02-26 (2) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + debug ENOBUFS + +- appearantly there are many 'incoming' sockets whose real ids were not assigned (but their socket + connections are kept) + > may need to check if incoming sockets handshake their ids properly + + +2013-02-25 (1) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + +- tested slowly joining nodes and let them run, seems more stable +- basic VON seems to work for up to 50 nodes, but then many nodes do not move + (appears to be disconnected) + +2013-02-23 (6) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + +- still can't pinpoint the problem... + +2013-02-19 (2) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + +- found out if nodes join slowly, then less likely to crash (can last longer) + + + +2013-02-05 (2) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + + + +2013-02-02 (6) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + +- found out the 'data' event and 'connect' event do not trigger in particular orders. + so might need to be careful to assume 'connect' always come before 'data' + + +2013-02-01 (5) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + +- after showing socket connection size and print out actually known sockets + found out too many sockets have negative socket numbers + (so likely there are over-connections every time a certain socket is the send target) + +2013-01-29 (2) +------------- +goal: debug ECONNABORTED (possibly over-connection), EFAULT + +BUGS known: +-0:36- [11] msgqueue has no target [2]. shouldn not happen +0:36- [16] got VON_HELLO_R from unknown neighbor [12] + + + +2013-01-27 (7) +------------- +goal: debug ECONNABORTED (possibly over-connection) + +- try to perform socket.end() whenever duplicate socket is detected +- new error: EFAULT + +net.js:912 + if (e.errno != EMFILE) throw e; ^ +Error: EFAULT, Bad address + at IOWatcher.callback (net.js:910:24) + + + +2013-01-26 (6) +------------- +goal: debug vast.io client + +- seems vast.io local client works now +- will have ECONNABORTED cause von_test_scale to crash with 50 nodes on Toshiba NB +- see lots of 'redundent socket already exists' warning + + +2013-01-22 (2) +------------- +goal: debug vast.io client + +- re-check & ensure that disconnect() on socket works + + +2013-01-21 (1) +------------- +goal: debug client id assignment + +- client id assign ok. +- need to debug vast.io client + +2013-01-20 (7) +------------- +goal: debug id assignment + support send to self id in vast_net's send() + +- debug gateway id assignment (now need to debug client ID assignment) +- also provide send to self in vast_net's send() + +2013-01-15 (2) +------------- +goal: debug id assignment + + +2013-01-12 (6) +------------- +goal: debug id assignment + remove id assign from net layer + +- removed id assign + +2013-01-11 (5) +------------- +goal: debug id assignment + +- need to remove id assignment from net layer + +2013-01-10 (4) +------------- +goal: debug id assignment + + +2013-01-08 (2) +------------- +goal: review _processData in vast_net for id assignment mechanism + +- implement id assigbment but is buggy +- need to debug id assignment + + +2013-01-05 (6) +------------- +goal: store sender id to all out-going messages at vast_net + +- modified send() in vast_net to accomodate sending multiple targets at once, + while attaching sender id + +- need to review _processData in vast_net for id assignment mechanism + + +2013-01-03 (4) +------------- +goal: store sender id to all out-going messages at vast_net + +- finished review net_nodejs +- found out need to decide where to pack/unpack sender id -2012-11-27 (1) + + +2012-12-30 (6) +------------- +goal: combine incoming & outgoing sockets into same management + test code + +- review net_nodejs & vast_net for correctness + +2012-12-22 (6) +------------- +goal: combine incoming & outgoing sockets into same management + check vast_net / net_nodejs modifications + test code + +- modify vast_net behavior to manage only sockets. + +2012-12-21 (5) +------------- +goal: combine incoming & outgoing sockets into same management + test code + +- figure out should manage all sockets in vast_net, but not net_nodejs objects. + + +2012-12-17 (1) +------------- +goal: combine incoming & outgoing sockets into same management + +- did code review/proofreading + +2012-12-16 (7) +------------- +goal: combine incoming & outgoing sockets into same management + +- finish remove _sockets in vast_net (pending tests) + + +2012-12-15 (6) +------------- +goal: modify msg_id so all messages' sender id is contained with message itself + +main issues now: +- need to allow remote node learn of id --> notify / contain id +- handshake & regular messages can be not sequential --> consider self-contain id +- redundent connections for same host:port pairs --> store out & in-coming sockets into same reference +- messages can pack up (queued up & send all at once) --> consider UDP + + + + +2012-12-14 (5) +------------- +goal: debug received NaN for sender at net layer + +- ensure that the message received is turned into js object first, before the 'sender id' (src) is retrieved. + now the NaN problem is solved. :) + +- now can run 50 nodes for a long time (10min+) However, two issues are observed: + 1. once 50 nodes are joined, it's difficult (almost impossible) to join a new node via web + 2. node movement in demo is very slow & not smooth + 3. nodes get discovered all at once + 4. many messages are accumulated. (change to UDP?) + + +2012-12-13 (4) +------------- +goal: debug received NaN for sender at net layer + experiment with include 'sender id' in all messages sent + +- try adding 'sender id', but still seems to get NaN id problems. Really need to look into the mechanism + for connections. + +2012-12-12 (3) +------------- +goal: debug received NaN for sender at net layer + +- found that the problem with dual-connection is not easy to solve. A case might go like this: + + 1. A sends hello to B (making new connection...) + 2. B sends hello to A (making new connection...) + 3. A rejects B's 'notify id' (there's already a connection established in 1.) + 4. B's message to A cannot be processed (source unknown 'NaN' problem) for the following: + 5: HELLO + 7: EN + 10: MOVE_B + 10: MOVE_B + 10: MOVE_B + 6: HELLO_R + 4: NODE + + why does 3 occur? (in theory, id notify goes only via a newly established connection...) + need to think more carefully about the dual-connection issue... + +- consider to enclose sender id in packet (makes it completely self-contained), so that it doesn't + matter which channel is used to deliver the message. This will be more RESTful? (and possibly can + become a HTTP-style protocol in future)? + + +2012-12-11 (2) +------------- +goal: debug received NaN for sender at net layer + +- found out NaN problem may be caused by id notify is sent to an already-established + socket connection. + + +2012-12-08 (6) +------------- +goal: debug received NaN for sender at net layer + +- try to set socket.connected flag *after* connect notify callback is called, + so that id notify may not be sent after other messages. + > does not solve the NaN id problem. + + +2012-12-06 (4) +------------- +goal: debug received NaN for sender at net layer + +- found out the bug is caused by ID notify and subsequent messages are mixed in sending order. + so later messages are received before the ID notify is processed. need to find a way to ensure + ID notify is sent absolutely first before all other messages. + > put ID notify to beginning of msgqueue to be sent to remote node, but NaN problem still exists... + > an explanation is the socket.write() does not actually preserve the order of writing, + > if message is put to system buffer due to too busy.. + +- found another problem is caused when two nodes initiate connections to each other simutaneously... + so possibly two channels are established... + > need to find a way to ensure just one connection + +- yet another problem is messages are sent from existing to new node in this order: + HELLO, EN, NODE, id notify.. + + possiby acceptor first discovers new node, then got VON_JOIN? or acceptor + another possibility is that the messages come from different connections + (one from self, another from remote). + + +2012-12-05 (3) +------------- +goal: debug received NaN for sender at net layer + +- notice that when nodes are joining quickly, VON_HELLO can be received earlier than VON_NODE for a joining node + (that means node is not yet fully joined, but other neighbors are already saying HELLO)... + +- change sending initial ID to wait till socket.write is complete. +- also change all messages can be processed once initial ID is obtained + +- bug still exists, looks like we need a more guaranteed way to notify unique ID + + +2012-12-03 (1) +------------- +goal: remove tick() from VON_peer.js + +- done removing tick() and spread original tick functions to after receiving VON_NODE + (contactNewNeighbor) and VON_MOVE_X (checkNeighborDiscovery & removeNonOverlapped) + + + + +2012-12-01 (6) +------------- +goal: implement subscribe() in VAST.js (matcher-side) + +BUG: if node positions overlap, the two nodes will continously have check enclosing neigbhor + error (as actual neighbor size is smaller than expected # of neighbors) + + + + +2012-11-29 (4) +------------- +goal: implement subscribe() in VAST.js (matcher-side) + extract non-standard code from init() in VON_Peer + +- revise init & join responisbility, and make init simply to initialize, + any network contact is done only at the join() stage + +- finish revision & testing (but on small NB, running 50 nodes will still cause + program fault/hang after a while, likely due to network problem). + + +2012-11-28 (3) +------------- +goal: implement subscribe() in VAST.js (matcher-side) + +- identfiy a potential issue: as init in VON_Peer.js performs necessary tasks other + than msg_handler creation, it's required to include it, but then msg_handler will + be created twice when VAST_matcher tries to include a VON_Peer inside itself. + solution seems to keep init() simple so it's the same for VON_Peer, VON_matcher, or VAST + +2012-11-27 (2) ------------- goal: fix port does not close after revokeNode is called in VSS +- added shut() to when VSS server performs revokeNode (seems to solve the issue) +BUG: when running test_VON_scale with 50 nodes, and shutdown all 50 nodes, + some nodes do not seem to disappear cleanly and still exist in the knoweldge of still-existing nodes + (need to somehow find a way to clean them, or to periodically ping / verify their existence) + + 2012-11-26 (1) ------------- @@ -1863,16 +2236,16 @@ m = (y2-y1) / (x2-x1) intercept: (y2-y1)/(x2-x1)*x1 + b = y1 -y = y1 + [(y2 - y1) / (x2 - x1)]¡P(x - x1) -(x2 - x1)¡P(y - y1) = (y2 - y1)¡P(x - x1) +y = y1 + [(y2 - y1) / (x2 - x1)]�P(x - x1) +(x2 - x1)�P(y - y1) = (y2 - y1)�P(x - x1) -(x2¡Py - x1¡Py) - (x2¡Py1 - x1¡Py1) = (x¡Py2 - x¡Py1) - (x1¡Py2-x1¡Py1) -x2¡Py - x1¡Py - x2¡Py1 + x1¡Py1 = x¡Py2 - x¡Py1 - x1¡Py2 + x1¡Py1 -(y2-y1)x + (x1-x2)y + (x2¡Py1 - x1¡Py1 - x1¡Py2 + x1¡Py1) = 0 +(x2�Py - x1�Py) - (x2�Py1 - x1�Py1) = (x�Py2 - x�Py1) - (x1�Py2-x1�Py1) +x2�Py - x1�Py - x2�Py1 + x1�Py1 = x�Py2 - x�Py1 - x1�Py2 + x1�Py1 +(y2-y1)x + (x1-x2)y + (x2�Py1 - x1�Py1 - x1�Py2 + x1�Py1) = 0 a = (y2-y1) b = (x1-x2) -c = (x2¡Py1 - x1¡Py1 - x1¡Py2 + x1¡Py1) +c = (x2�Py1 - x1�Py1 - x1�Py2 + x1�Py1) - contains() work with rh_voronoi, but still buggy (check returns 'true' very often) @@ -5416,7 +5789,7 @@ DEBUG: 1) an annoying bug where many times, the program would crash within sfvoronoi, where doing a free (buf_list[]) in calvs() tries to free invalid buf_list. - the bug (found by ¤Öºd) was that in processmsg of VAST, we'd collect stat info of msgtype + the bug (found by �ֺd) was that in processmsg of VAST, we'd collect stat info of msgtype via _msg_stat[msgtype] += size; @@ -6003,13 +6376,13 @@ goal: review simulation code for potential unstability, (possibily removing the scalability analysis paragraph in VON paper) - made a decision today to let another person work on the simulation in full. - today asked 泓ç? to help. + today asked 泓�? to help. 2006/03/14 (2) -------------- -- today asked å°‘æ? to help on NAT. +- today asked 少�? to help on NAT. 2006/03/13 (1) -------------- diff --git a/VAST.js b/VAST.js index b26c5441..a5e9258c 100644 --- a/VAST.js +++ b/VAST.js @@ -26,7 +26,6 @@ supported functions: // basic callback / structure - settings = {port}; pos = {x, y} addr = {host, port} area = {pos, radius} @@ -38,7 +37,7 @@ recv_CB(msg) // callback to receive any messages // constructor - VAST(recv_CB, settings, GW_addr) + VAST(recv_CB, GW_addr) // basic functions subscribe(layer, area, sub_CB) subscribe for an area at a given layer @@ -79,7 +78,7 @@ var TIMEOUT_SUBSCRIBE = (5); // number of seconds before re-attempt //var TIMEOUT_REMOVE_GHOST = (5); // # of seconds before removing ghost objects at clients -function VAST_client(recv_callback, settings, GW_addr) { +function VAST_client(recv_callback, GW_addr) { // callback to notify subscribed messages received var _recv_CB = recv_callback; diff --git a/VAST_matcher.js b/VAST_matcher.js index e1cffadb..361323d0 100644 --- a/VAST_matcher.js +++ b/VAST_matcher.js @@ -61,13 +61,13 @@ function VAST_matcher(recv_callback, settings) { // public methods // - // init/join the matcher? + // join the matcher to a VON this.join = function (done_CB) { // join VON network var id, port; - _self.join(id, port, function () { + _self.init(id, port, function () { // when done }); @@ -346,7 +346,7 @@ function VAST_matcher(recv_callback, settings) { self_id = self_id || VAST_ID_UNASSIGNED; port = port || VAST_DEFAULT_PORT; - // create new layer + // create message handler manager and add self as one of the handlers var handler = new msg_handler(self_id, port, function (local_addr) { // NOTE: this will cause initStates() be called @@ -375,12 +375,7 @@ function VAST_matcher(recv_callback, settings) { _sendPack = _msg_handler.sendPack; // add VON peer as self node - _self = new VON.peer(); - - // provide self id, self port, gateway address, and callback when done - // TODO: how to init a matcher? - //_self.init(id, - + _self = new VON.peer(); _msg_handler.addHandler(_self); } } diff --git a/VON_peer.js b/VON_peer.js index 8c3afd4d..55793a07 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -37,7 +37,7 @@ vast_net (see definition in vast_net.js) // constructoracceptor for a given center point - join(aoi, done_CB) join a VON network with a given aoi + join(addr, aoi, done_CB) join a VON network at a gateway with a given aoi leave() leave the VON network move(aoi, send_time) move the AOI to a new position (or change radius) list() get a list of AOI neighbors @@ -46,7 +46,7 @@ VON_peer(aoi_buffer, aoi_use_strict) // basic functions - init(id, port, addr, done_CB) init a VON peer with id, listen port & gateway's address + init(id, port, done_CB) init a VON peer with id, listen port shut() shutdown a VON peer (will close down listen port) query(center, acceptor_CB) find the re a app-specific data along with the node (will pass during node discovery) get() retrieve app-specific data for this node @@ -145,35 +145,20 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { var _that = this; // function to create a new net layer - this.init = function (self_id, port, GW_addr, done_CB) { + this.init = function (self_id, port, done_CB) { self_id = self_id || VAST_ID_UNASSIGNED; port = port || VON_DEFAULT_PORT; - - LOG.debug('VON_peer init called, self_id: ' + self_id + ' port: ' + port); - + // create new layer - _msg_handler = new msg_handler(self_id, port, function (local_addr) { + var handler = new msg_handler(self_id, port, function (local_addr) { // NOTE: this will cause initStates() be called - _msg_handler.addHandler(_that); - - // store gateway address, ensure input conforms to internal data structure - // NOTE: need to do it here, as _storeMapping is not effective after addHandler calls initStates - var addr = new VAST.addr(); - addr.parse(GW_addr); - _storeMapping(VAST_ID_GATEWAY, GW_addr); - - LOG.warn('VON_peer init: gateway set to: ' + addr.toString()); + handler.addHandler(_that); - // store callback after gateway response - _init_done_CB = done_CB; - - // send PING to gateway to learn of my id - if (self_id === VAST_ID_UNASSIGNED) - _sendMessage(VAST_ID_GATEWAY, VON_Message.VON_PING, {request: true}, VAST.priority.HIGHEST); - else - _setInited(); + // notify done + if (typeof done_CB === 'function') + done_CB(local_addr); }); } @@ -185,10 +170,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // NOTE: if VON peer is used with other nodes, stopping server here will impact other nodes as well _msg_handler.close(); _msg_handler = undefined; - } - - _self = undefined; - + } _state = VAST.state.ABSENT; } @@ -208,13 +190,8 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { } // join a VON network with a given aoi - var _join = this.join = function (aoi, done_CB) { - - if (_state === VAST.state.ABSENT) { - LOG.error('VON_peer.join(): node not yet init'); - return; - } - + var _join = this.join = function (GW_addr, aoi, done_CB) { + // check if already joined if (_state === VAST.state.JOINED) { LOG.warn('VON_peer.join(): node already joined'); @@ -223,49 +200,32 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { return; } - LOG.debug('VON_peer join() called'); - + LOG.debug('VON_peer join() called, self: ' + _self.id + ' getID: ' + _getID()); + + // store gateway address, ensure input conforms to internal data structure + // NOTE: need to do it here, as _storeMapping is not effective after addHandler calls initStates + var addr = new VAST.addr(); + addr.parse(GW_addr); + _storeMapping(VAST_ID_GATEWAY, addr); + LOG.warn('gateway set to: ' + addr.toString()); + + // store initial aoi + _self.aoi.update(aoi); + // change internal state _state = VAST.state.JOINING; // keep reference to call future once join is completed _join_done_CB = done_CB; - - // set self AOI - _self.aoi.update(aoi); - // update address info for self - var addr = _msg_handler.getAddress(); - _self.endpt = new VAST.endpt(addr.host, addr.port); - - // store self node to neighbor map - _insertNode(_self); - - // if I'm gateway, no further action required - // TODO: doesn't look clean, can gateway still send query request to itself? - // that'll be a more general process - // (however, will deal with how to determined 'already joined' for gateway) - if (_self.id === VAST_ID_GATEWAY) - return _setJoined(); - - // build node info for acceptor to contact back (from _self) - var joiner = new VAST.node(); - - //LOG.debug('joiner (before): ' + joiner.toString()); - - joiner.update(_self); - joiner.aoi.center = aoi.center; - - //LOG.debug('joiner (after): ' + joiner.toString()); - LOG.debug('joiner: ' + joiner.toString()); - - // send out query request first to find acceptor - _query(VAST_ID_GATEWAY, aoi.center, VON_Message.VON_JOIN, joiner); - - // TODO: if id is not correct, remote host will send back correct one - //_sendMessage(VAST_ID_GATEWAY, VON_Message.VON_QUERY, _self, VAST.priority.HIGHEST); + // if id is empty, send PING to gateway to learn of my id first + // otherwise attempt to join by contacting gateway + if (_getID() === VAST_ID_UNASSIGNED) + _sendMessage(VAST_ID_GATEWAY, VON_Message.VON_PING, {request: true}, VAST.priority.HIGHEST); + else + _setInited(); } - + // leave the VON network var _leave = this.leave = function () { @@ -291,17 +251,19 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // clean data structure _initStates(); + /* // remove ticking interval // TODO: remove js-specific code or collect them if (_interval_id !== undefined) { clearInterval(_interval_id); _interval_id = undefined; } + */ } // move the AOI to a new position (or change radius) var _move = this.move = function (aoi, sendtime) { - LOG.debug('VON_Peer moving to: ' + aoi.center.toString()); + //LOG.warn('VON_Peer moving to: ' + aoi.center.toString()); try { if (typeof aoi.radius === 'string') @@ -397,7 +359,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // store a app-specific data along with the node (will pass during node discovery) var _put = this.put = function (obj) { _meta = obj; - LOG.warn('after put() meta keys: ' + Object.keys(_meta).length); + LOG.debug('after put() meta keys: ' + Object.keys(_meta).length); // update to self (so that it can be sent over) _self.meta = _meta; @@ -405,7 +367,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // retrieve app-specific data for this node var _get = this.get = function () { - LOG.warn('get() meta keys: ' + Object.keys(_meta).length); + LOG.debug('get() meta keys: ' + Object.keys(_meta).length); return _meta; } @@ -599,6 +561,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // // the master function for all things regular + /* var _tick = function () { //_sendKeepAlive(); @@ -611,33 +574,47 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { LOG.error('tick error:\n' + e.stack); } } + */ + + // self creation once self ID is obtained + var _setInited = function () { + + // update id & address info for self (we need valid self address for acceptor to contact) + _self.id = _getID(); + var addr = _msg_handler.getAddress(); + _self.endpt = new VAST.endpt(addr.host, addr.port); + + LOG.warn('init myself as ' + _self.toString()); + + // store self node to neighbor map + _insertNode(_self); + + // if I'm gateway, no further action required + // TODO: doesn't look clean, can gateway still send query request to itself? + // that'll be a more general process + // (however, will deal with how to determined 'already joined' for gateway) + if (_self.id === VAST_ID_GATEWAY) + _setJoined(); + else + // send out query request first to find acceptor + _query(VAST_ID_GATEWAY, _self.aoi.center, VON_Message.VON_JOIN, _self); + } // set current node to be 'joined' + // NOTE: should not do other init stuff (such as insert self to neighbor list) here + // otherwise VON_NODE message may be incorrectly processed var _setJoined = function () { _state = VAST.state.JOINED; - - // notify callback + + // notify join is done if (typeof _join_done_CB === 'function') _join_done_CB(_self.id); // start ticking - _interval_id = setInterval(_tick, TICK_INTERVAL); + //_interval_id = setInterval(_tick, TICK_INTERVAL); } - - // set current node to be 'init' - var _setInited = function () { - - _state = VAST.state.INIT; - // NOTE: other fields (endpt, aoi, time) may be empty at this point - _self = new VAST.node(_getID()); - - // notify done - if (typeof _init_done_CB === 'function') - _init_done_CB(_self.id); - } - var _sendKeepAlive = function () { // simply move a little to indicate keepalive @@ -694,7 +671,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // if the neighbor is relevant and we insert it successfully // NOTE that we're more tolerant for AOI buffer when accepting notification - if (_isRelevantNeighbor (node, _self)) { + if (_isRelevantNeighbor(node, _self)) { LOG.debug('[' + node.id + '] is relevant to self [' + _self.id + ']'); @@ -715,8 +692,8 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // NOTE: even if en_list is empty, should still send out HELLO // empty can occur if initially just two nodes with overlapped positions - _sendHello(target); - _sendEN(target); + //_sendHello(target); + //_sendEN(target); } } @@ -725,8 +702,14 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _voro.remove(new_list[i]); // insert new neighbors - for (var i=0; i < relevant_neighbors.length; i++) + for (var i=0; i < relevant_neighbors.length; i++) { _insertNode(relevant_neighbors[i]); + + // NOTE: even if en_list is empty, should still send out HELLO + // empty can occur if initially just two nodes with overlapped positions + _sendHello(relevant_neighbors[i].id); + _sendEN(relevant_neighbors[i].id); + } // NOTE: erase new neighbors seems to bring better consistency // (rather than keep to next round, as in previous version) @@ -868,12 +851,19 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // remove neighbors no longer in view var _removeNonOverlapped = function () { - - //vector delete_list; - var delete_list = []; var now = UTIL.getTimestamp (); + // check if we should perform check (should check no more than once per second) + if ((now - _lastRemoveNonOverlapped) < 1000) + return 0; + + // update check time + _lastRemoveNonOverlapped = now; + + // prepare neighbor list to remove + var delete_list = []; + // wait time in milliseconds var grace_period = now + (MAX_DROP_SECONDS * 1000); @@ -937,7 +927,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // list: a list of id to be sent var _sendHello = function (target) { - LOG.debug('sendHello target: ' + target); + //LOG.warn('[' + _self.id + '] sendHello to [' + target + ']'); var node = _getNode(_self.id); @@ -946,7 +936,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // add app-specific attributes, if exist // TODO: find a cleaner way (for example, meta already exists in the node returned by getNode) if (Object.keys(_meta).length > 0) { - LOG.warn('storing meta keys ' + Object.keys(_meta).length + ' to node to be sent for VON_HELLO'); + LOG.debug('storing meta keys ' + Object.keys(_meta).length + ' to node to be sent for VON_HELLO'); node.meta = _meta; } @@ -990,30 +980,71 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // handlers for incoming messages, connect/disconnect // + // new ID assignment + var _new_ID = undefined; + var _assignNewID = function () { + + // we use our own ID as first + // NOTE if we start with VAST_ID_UNASSIGNED (0) then first ID will be 1 + if (_new_ID === undefined) + _new_ID = _getID() + 1; + + LOG.warn('new ID assigned: ' + _new_ID); + return _new_ID++; + } + var _packetHandler = this.packetHandler = function (from_id, pack) { - // if node is not initiated, do not process any message, unless it's a PING response - if (_state < VAST.state.INIT) { - - if (pack.type !== VON_Message.VON_PING) { - LOG.error('VON_peer: node not yet init, should not process any messages'); + // if node is not joined, do not process any message, unless it's response for + // getting ID (VON_PING) or neighbor list (VON_NODE) or handshake (VON_HELLO) + // NOTE: it's possible for VON_HELLO to arrive earlier than VON_NODE + /* + if (_state < VAST.state.JOINED) { + + if (pack.type !== VON_Message.VON_PING && + pack.type !== VON_Message.VON_NODE && + pack.type !== VON_Message.VON_HELLO) { + LOG.error('VON_peer: node not yet joined, should not process any messages'); return false; } - - _setInited(); } + */ - LOG.debug('[' + _self.id + '] ' + VON_Message_String[pack.type] + ' from [' + from_id + '], neighbor size: ' + Object.keys(_neighbors).length); + if (_self.id === VAST_ID_UNASSIGNED && pack.type !== VON_Message.VON_PING) { + LOG.error('VON_peer: node not yet init (got unique ID), should not process any messages'); + return false; + } + + LOG.debug('[' + _self.id + '] ' + VON_Message_String[pack.type] + ' from [' + from_id + '], neighbor size: ' + Object.keys(_neighbors).length); switch (pack.type) { // VON's ping, to check if a connected host is still alive case VON_Message.VON_PING: { - + // check if it's a request, respond to it if (typeof pack.msg.request !== 'undefined' && pack.msg.request == true) { - LOG.warn('VON_PING receive request, simply respond'); - _sendMessage(from_id, VON_Message.VON_PING, {request: false}, VAST.priority.HIGH, true); + + // if remote id is not yet assigned, assign new one + var remote_id = _assignNewID(); + LOG.warn('assign new ID [' + remote_id + '] to [' + from_id + ']'); + + // check if this is the first ever ID assigned by me + // if so, then I'm likely the gateway (my ID is also unassigned yet) + if (_self.id === VAST_ID_UNASSIGNED) { + LOG.warn('first ID assigned, likely I am the gateway'); + _self.id = remote_id; + } + + //LOG.warn('VON_PING receive request, simply respond'); + _sendMessage(from_id, VON_Message.VON_PING, {request: false, aid: remote_id}, VAST.priority.HIGH, true); + } + // otherwise we got a response from gateway, set my ID, can now send join request + else if (_self.id === VAST_ID_UNASSIGNED) { + var assigned_id = parseInt(pack.msg.aid); + LOG.debug('assigned_id: ' + assigned_id); + _setID(assigned_id); + _setInited(); } } break; @@ -1029,17 +1060,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // find the node closest to the joiner var closest = _voro.closest_to(pos); LOG.debug('closest node: ' + closest + ' (' + typeof closest + ')'); - - // TODO: should generate a VON_JOIN directly to self - /* - // if this is gateway receiving its own request - if (closest === VAST_ID_UNASSIGNED && Object.keys(_neighbors).length === 1) { - LOG.warn('gateway getting its own VON_QUERY, return empty list'); - _sendNodes (from_id, [], true); - break; - } - */ - + // forward the request if a more appropriate node exists // TODO: contains() might recompute Voronoi, isRelevantNeighbor below // also will recompute Voronoi. possible to combine into one calculation? @@ -1047,7 +1068,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _isSelf(closest) === false && closest != from_id) { - LOG.warn('forward VON_QUERY request to: ' + closest); + //LOG.warn('forward VON_QUERY request to: ' + closest); // re-assign target for the query request pack.targets = []; @@ -1056,7 +1077,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { } else { - LOG.warn('accepting VON_QUERY from: ' + from_id); + LOG.debug('accepting VON_QUERY from: ' + from_id); // I'm the acceptor, re-direct message content to self pack.type = pack.msg.type; @@ -1082,11 +1103,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // VON's join, to learn of initial neighbors case VON_Message.VON_JOIN: { - - // update the joiner's id (if it's a newly joined node) - if (pack.msg.id === VAST_ID_UNASSIGNED) - pack.msg.id = from_id; - + // extract message var joiner = new VAST.node(); joiner.parse(pack.msg); @@ -1175,17 +1192,20 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _new_neighbors[new_node.id] = new_node; } } // end going through node list - + + // process new neighbors immediately after learning new nodes + _contactNewNeighbors(); + // if VON_NODE is received for the first time, we're considered joined if (_state === VAST.state.JOINING) { // process new neighbors if we just joined, // otherwise, do this periodically & collectively during tick - _contactNewNeighbors(); + //_contactNewNeighbors(); // NOTE: we don't notify join success until neighbor list is processe // so that upon join success, the client already has a list of neighbors - _setJoined(); + _setJoined(); } } break; @@ -1203,7 +1223,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // store meta data, if any if (pack.msg.hasOwnProperty('meta') && typeof pack.msg.meta === 'object') { - LOG.warn('VON_HELLO storing to node meta keys: ' + Object.keys(pack.msg.meta).length); + LOG.debug('VON_HELLO storing to node meta keys: ' + Object.keys(pack.msg.meta).length); node.meta = pack.msg.meta; } @@ -1246,7 +1266,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // store meta data, if any if (pack.msg.hasOwnProperty('meta') && typeof pack.msg.meta === 'object') { - LOG.warn('meta keys received: ' + Object.keys(pack.msg.meta).length); + LOG.debug('meta keys received: ' + Object.keys(pack.msg.meta).length); neighbor.meta = pack.msg.meta; } @@ -1350,8 +1370,10 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _req_nodes[from_id] = true; // check for neighbor discovery - // NOTE: do this only periodically during tick - //_checkNeighborDiscovery(); + _checkNeighborDiscovery(); + + // remove neighbors I'm no longer interested + _removeNonOverlapped(); } break; @@ -1411,11 +1433,12 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _msg_handler = msg_handler; var id = _msg_handler.getID(); - LOG.warn('VON_peer initStates called with msg_handler, id: ' + id); + //LOG.warn('VON_peer initStates called with msg_handler, id: ' + id); // add convenience references _storeMapping = _msg_handler.storeMapping, _getID = _msg_handler.getID, + _setID = _msg_handler.setID, _disconnect = _msg_handler.disconnect, _sendMessage = _msg_handler.sendMessage, _sendPack = _msg_handler.sendPack; @@ -1436,6 +1459,8 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _updateStatus = {}; _req_nodes = {}; _time_drop = {}; + + _lastRemoveNonOverlapped = 0; // create an object to calculate Voronoi diagram for neighbor mangement/discovery _voro = new Voronoi(); @@ -1473,15 +1498,15 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { var _msg_handler = undefined; // convenience references - var _storeMapping, _getID, _disconnect, _sendMessage, _sendPack; + var _storeMapping, _getID, _setID, _disconnect, _sendMessage, _sendPack; // // internal states // // reference to self (NOTE: need to be initialized in init()) - var _self; - + var _self = new VAST.node(); + // callback to use once join is successful var _join_done_CB = undefined; @@ -1489,7 +1514,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { var _init_done_CB = undefined; // interval id for removing periodic ticking - var _interval_id = undefined; + //var _interval_id = undefined; // node state definition var _state; @@ -1505,6 +1530,8 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { var _updateStatus; // status about whether a neighbor is: 1: inserted, 2: deleted, 3: updated var _time_drop; + + var _lastRemoveNonOverlapped; // last timestamp when removeNonOverlapped is called // create an object to calculate Voronoi diagram for neighbor mangement/discovery var _voro; diff --git a/VSS/VSS.js b/VSS/VSS.js index caef2964..c6f4b623 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -3,7 +3,7 @@ require('../common'); // do not show debug -LOG.setLevel(2); +//LOG.setLevel(2); var server = require("./server"); var router = require("./router"); diff --git a/VSS/handler.js b/VSS/handler.js index e268bf8c..d6de5874 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -52,11 +52,10 @@ var _registerNode = function (pos, info, done_CB) { var new_node = new VON.peer(); // join in the network - new_node.init(VAST_ID_UNASSIGNED, ip_port.port + _nodes_created, ip_port, function () { + new_node.init(VAST_ID_UNASSIGNED, ip_port.port + _nodes_created, function () { _nodes_created++; - LOG.debug('new_node (before join): ' + new_node.getSelf().toString()); - + // store node ident for ident discovery across different VSS servers var ident_info = { apikey: info.apikey, @@ -66,13 +65,13 @@ var _registerNode = function (pos, info, done_CB) { new_node.put(ident_info); - new_node.join(aoi, + new_node.join(ip_port, aoi, // done callback function (self_id) { LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + new_node.getSelf().id); - + // keep track of newly joined node in internal record if (_keys.hasOwnProperty(info.apikey) === false) _keys[info.apikey] = {}; diff --git a/common.js b/common.js index f3b15d52..19e12b8e 100644 --- a/common.js +++ b/common.js @@ -29,7 +29,7 @@ global.VAST.matcher = require('./VAST_matcher'); // TODO: find a better way to store this? (maybe in msg_handler?) global.VAST.state = { ABSENT: 0, - INIT: 1, // init done + // INIT: 1, // init done JOINING: 2, // different stages of join JOINED: 3 }; diff --git a/common/logger.js b/common/logger.js index e1e03745..7d7a46f2 100644 --- a/common/logger.js +++ b/common/logger.js @@ -12,16 +12,23 @@ var _red = '\033[31m'; var _white = '\033[m'; var _yellow = '\033[33m'; -var _ERR = _red + 'ERR -'; -var _ERREND = _white; - -var _WARN = _yellow + '-'; +var _ERR = _red; +var _ERREND = _white +var _WARN = _yellow; function logger() { + var currDate = new Date(); + var dateStr = '-' + currDate.getHours() + ':' + currDate.getMinutes() + '- '; + // by default we display all var _level = 3; + // convert obj to msg + var _convert = function (obj) { + return (typeof obj === 'object' ? JSON.stringify(obj, null, 4) : obj); + } + // set log level: 1 (error only), 2 (warning), 3 (debug) this.setLevel = function (level) { if (level <= 0 || level > 3) { @@ -29,22 +36,25 @@ function logger() { return; } _level = level; - this.debug('set error level to be: ' + level); + console.log('set error level to be: ' + level); } this.debug = function (msg) { + msg = _convert(msg); if (_level >= 3) - console.log(msg); + console.log(dateStr + msg); } this.warn = function (msg) { + msg = _convert(msg); if (_level >= 2) - console.log(_WARN + msg + _ERREND); + console.log(_WARN + dateStr + msg + _ERREND); } this.error = function (msg) { + msg = _convert(msg); if (_level >= 1) - console.log(_ERR + msg + _ERREND); + console.log(_ERR + dateStr + msg + _ERREND); } this.stack = function () { diff --git a/net/msg_handler.js b/net/msg_handler.js index ee356c5e..8bdb7b7f 100644 --- a/net/msg_handler.js +++ b/net/msg_handler.js @@ -54,6 +54,7 @@ // accessors getAddress get locally detected & binded network address getID get unique ID for the net layer + setID set unique ID for the net layer history: 2012-10-03 initial version @@ -129,9 +130,7 @@ function msg_handler(l_self_id, l_listen_port, l_onDone) { priority); pack.targets.push(target); - - //console.log('_net: ' + _net + ' target: ' + target); - + // convert pack to JSON or binary string return _that.sendPack(pack, is_reliable); } @@ -142,22 +141,11 @@ function msg_handler(l_self_id, l_listen_port, l_onDone) { // do nothing is target is empty if (pack.targets.length === 0) return; - - // serialize string - var encode_str = JSON.stringify(pack); - + // go through each target and send - // TODO: optimize so only one message is sent to each physical host target - var to_list = ''; - var self_id = _net.getID(); - - for (var i=0; i < pack.targets.length; i++) { - - _net.send(pack.targets[i], encode_str, is_reliable); - to_list += (pack.targets[i] + ','); - } - - LOG.debug('SEND to [' + to_list + ']: ' + encode_str); + // TODO: optimize so only one message is sent to each physical host target + //for (var i=0; i < pack.targets.length; i++) + _net.send(pack.targets, pack, is_reliable); } // store a network id to address mapping @@ -184,7 +172,13 @@ function msg_handler(l_self_id, l_listen_port, l_onDone) { this.getID = function () { return _net.getID(); } - + + // set self ID to net layer + this.setID = function (id) { + return _net.setID(id); + } + + // // private methods (internal usage, some are replacable at inherted class) // @@ -204,28 +198,8 @@ function msg_handler(l_self_id, l_listen_port, l_onDone) { // as they need to occupy memory independently for each msg handler instance // handler for incoming messages - var _packetHandler = function (from_id, msg) { - - // prevent processing invalid msg - // NOTE: we allow for empty message (?) such as VON_DISCONNECT - if (msg == '' || msg === null || msg === undefined) { - LOG.error('msgHandler: invalid msg from [' + from_id + '], skip processing'); - return; - } - - LOG.debug('RECV from [' + from_id + ']: ' + msg); - - var pack; - - try { - // convert msg back to js_obj - pack = JSON.parse(msg); - } - catch (e) { - LOG.error('msgHandler: convert to js_obj fail: ' + e + ' msg: ' + msg); - return; - } - + var _packetHandler = function (from_id, pack) { + // go through each packet handler and see which will handle it for (var i=0; i < _handlers.length; i++) { if (typeof _handlers[i].packetHandler === 'function') @@ -233,9 +207,9 @@ function msg_handler(l_self_id, l_listen_port, l_onDone) { // successfully handle incoming packet, return return true; } - } + } - LOG.error('no packet handler for message type: ' + pack.type + ' msg: ' + msg); + LOG.error('[' + _net.getID() + '] no packet handler for packet: ' + JSON.stringify(pack)); return false; } @@ -267,7 +241,7 @@ function msg_handler(l_self_id, l_listen_port, l_onDone) { LOG.debug('msg_handler init, l_self_id: ' + l_self_id); // create new net layer if does not exist - LOG.warn('creating new VASTnet... net: ' + typeof _net); + //LOG.warn('creating new VASTnet... net: ' + typeof _net); // create network layer & start listening // NOTE: internal handlers must be defined before creating the VAST.net instance diff --git a/net/net_nodejs.js b/net/net_nodejs.js index 7f1daa70..b7ca1303 100644 --- a/net/net_nodejs.js +++ b/net/net_nodejs.js @@ -22,7 +22,7 @@ /* net_nodejs.js - A network layer usable under node.js + A network connector usable under node.js ///////////////////////////// data structure / callback: @@ -46,18 +46,11 @@ // make TCP connection to a given host_port // all received messages from this socket is return to 'onReceive' // connection & disconnection can be notified via callbacks (optional) - connect(host_port, data_callback, onConnect, onDisconnect) + connect(host_port) // disconnect current connection disconnect() - - // send a message 'msg' of length 'size' to current connection, - // optional 'socket' can be provided to respond requests from incoming clients - send(msg, socket) - - // send message 'msg' to a UDP channel (given by 'host_port') - send_udp(host_port, msg); - + // listen to a particular port for incoming connections / messages // all received messages are delivered to 'onReceive' // return port binded, or 0 to indicate error @@ -86,7 +79,10 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { var _server = undefined; // a client object for making connection - var _client = undefined; + var _socket = undefined; + + // reference + var _that = this; // check for connection validity and send to it) // ip_port is an object with 'host' and 'port' parameter @@ -96,28 +92,31 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { try { // connect to the given host & port, while providing a connection listener - _client = l_net.createConnection(host_port.port, host_port.host); + _socket = l_net.createConnection(host_port.port, host_port.host); - _client.on('connect', function () { + _socket.on('connect', function () { - LOG.debug('out connect success for: ' + host_port.host + ':' + host_port.port); - - // store remote address & port - _client.host = host_port.host; - _client.port = host_port.port; - _client.connected = true; - - if (typeof onConnect === 'function') - onConnect(_client); - + // setup connected socket // NOTE: the handler for 'connect' inside setupSocket won't be called, // as 'connect' event is already triggered here, so whatever task needs to be duplicated here // TODO: more elegant approach? - setupSocket(_client); - }); + setupSocket(_socket); + + LOG.debug('out connect success for: ' + host_port.host + ':' + host_port.port); + + // store remote address & port + _socket.host = host_port.host; + _socket.port = host_port.port; + _socket.disconnect = _that.disconnect; + _socket.connected = true; + + if (typeof onConnect === 'function') + onConnect(_socket); + }); + // if there's error on the connection, 'close' will follow - _client.on('error', function(err){ + _socket.on('error', function(err){ LOG.error('out connect socket error: ' + err); }); @@ -131,55 +130,23 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // disconnect from a given socket id this.disconnect = function () { - if (_client === undefined) + if (_socket === undefined) { + LOG.warn('net_nodejs: no socket established to disconnect'); return false; + } try { - LOG.debug('disconnecting from ' + _client.host + ':' + _client.port); - _client.end(); + LOG.debug('disconnecting from ' + _socket.host + ':' + _socket.port); + _socket.end(); } catch (e) { LOG.error('net_nodejs: disconnect error: ' + e); if (typeof onError === 'function') onError('disconnect'); } + return true; } - - // send a given message to a socket_id - this.send = function (msg, socket) { - //LOG.debug('sending to ' + socket + ' msg: ' + msg); - - try { - // if this is a connection - if (_client !== undefined) { - //LOG.debug('to client'); - _client.write(msg); - } - // if this is a listening server - else if (socket !== undefined) { - //LOG.debug('to socket'); - socket.write(msg); - } - // if neither - else - return false; - } - catch (e) { - LOG.debug('send fail for msg: ' + msg); - if (typeof onError === 'function') - onError('send'); - return false; - } - - return true; - } - - // send a given message to a UDP-style host:port - this.send_udp = function (host_port, msg) { - - } - // listen to a given port, while processing incoming messages at a callback this.listen = function (port, onDone) { @@ -240,8 +207,8 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // check if the socket is currently connected this.isConnected = function () { - //LOG.warn('connected: ' + _client.connected); - return (_client !== undefined && _client.connected === true); + //LOG.warn('connected: ' + _socket.connected); + return (_socket !== undefined && _socket.connected === true); } // setup a new usable socket with a socket handler @@ -259,15 +226,19 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // call data callback to process data, if exists // (this happens when setupSocket is called by a listening server for setup new socket) if (typeof onReceive === 'function') { - //LOG.debug('recv_CB provided, registered for data'); socket.on('data', function (data) { onReceive(socket, data); }); } - // when socket becomes empty again + // when socket becomes empty again, can now keep sending queued messages socket.on('drain', function () { - socket.resume(); + try { + socket.resume(); + } + catch (e) { + LOG.error('net_nodejs: resume error: ' + e.stack); + } }); // NOTE: this won't be triggered for out-going connections, only incoming (i.e., when a listening server calls this) @@ -278,13 +249,18 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { socket.host = socket.remoteAddress; socket.port = socket.remotePort; - LOG.debug('connection created: ' + socket.host + ':' + socket.port); - - socket.connected = true; + LOG.debug('in connect success for ' + socket.host + ':' + socket.port); + + socket.connected = true; + // attach convenience function + socket.disconnect = function () { + socket.end(); + } + // notify connection, pass the connecting socket if (typeof onConnect === 'function') - onConnect(socket); + onConnect(socket); }); // handle connection error or close @@ -307,7 +283,8 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { } // NOTE:: if remote host calls 'disconnect' to send a FIN packet, - // this host will receive 'end' directly (without getting a 'close' event) + // this host will receive 'end' directly + // (but will 'close' be emitted?) // see: http://nodejs.org/api/net.html#net_event_close_1 socket.on('end', function () { //LOG.warn('socket [end] called'); @@ -329,7 +306,7 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { }); // if there's error on the connection, 'close' will follow - socket.on('error', function(err){ + socket.on('error', function (err){ LOG.error('socket error: ' + err); }); diff --git a/net/vast_net.js b/net/vast_net.js index cd7dce6d..df57ab1b 100644 --- a/net/vast_net.js +++ b/net/vast_net.js @@ -26,29 +26,29 @@ // basic callback / structure addr = {host, port} - CB_receive(id, msg) - CB_connect(id) - CB_disconnect(id) + onReceive(id, msg) + onConnect(id) + onDisconnect(id) // constructor - vast_net(CB_receive, CB_connect, CB_disconnect); + vast_net(onReceive, onConnect, onDisconnect); // basic functions - storeMapping(id, addr) store mapping from id to a particular host IP/port - setID(new_id, old_id) set self ID or change the id -> socket mapping for incoming connections - getID() get self ID (which may be assigned by remote host) - send(id, msg, is_reliable) send a message to a target 'id' - listen(port, CB_done) start a server at a given 'port', port binded is returned via 'CB_done', 0 indicates error - close() close a server - disconnect(id) disconnect connection to a remote node - + storeMapping(id, addr) store mapping from id to a particular host IP/port + setID(new_id, old_id) set self ID or change the id -> socket mapping for incoming connections + getID() get self ID (which may be assigned by remote host) + send([id], msg, reliable, onDone) send a message to a target 'id', initiate connection as needed + listen(port, onDone) start a server at a given 'port', port binded is returned via 'onDone', 0 indicates error + close() close a server + disconnect(id) disconnect connection to a remote node + // socket related - id = openSocket(addr, recv_callback); + id = openSocket(addr, onReceive); closeSocket(id); sendSocket(id, msg); // aux helper methods (info from physical host) - getHost(CB_done); + getHost(onDone); // state check methods isJoined(); @@ -62,38 +62,38 @@ 2012-07-05 first working version (storeMapping, switchID, send) 2012-07-20 rename switchID -> setID (can set self ID) */ - -//require('../common.js'); +var os = require('os'); var l_net = require('./net_nodejs'); // implementation-specific network layer //var VAST_ID_UNASSIGNED = 0; // // input: -// CB_receive(id, msg) callback when a message is received -// CB_connect(id) callback when a remote host connects -// CB_disconnect(id) callback when a remote host disconnects -// id_generator() generator callback to create new IDs (optional & used only at gateway) +// onReceive(id, msg) callback when a message is received +// onConnect(id) callback when a remote host connects +// onDisconnect(id) callback when a remote host disconnects +// id optional assigned id // -function vast_net(CB_receive, CB_connect, CB_disconnect, id) { +function vast_net(onReceive, onConnect, onDisconnect, id) { // // aux methods // // get & store local IP - var _localIP = undefined; - + var _localIP = '127.0.0.1'; + //var _localIP = undefined; + // return the host IP for the current machine - this.getHost = function (CB_done) { + this.getHost = function (onDone) { - var hostname = require('os').hostname(); - LOG.debug('getHost called, hostname: ' + hostname); - // if already available, return directly if (_localIP !== undefined) - return CB_done(_localIP); - - + return onDone(_localIP); + + var hostname = os.hostname(); + LOG.debug('getHost called, hostname: ' + hostname); + + // NOTE: if network is not connected, lookup might take a long, indefinite time require('dns').lookup(hostname, function (err, addr, fam) { if (err) { @@ -103,8 +103,8 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { else _localIP = addr; - CB_done(_localIP); - }) + onDone(_localIP); + }) } // @@ -112,27 +112,23 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // // id for myself, created by an id_generator, if available - var _self_id = (typeof id === 'undefined' ? VAST_ID_UNASSIGNED : id); + var _self_id = id || VAST_ID_UNASSIGNED; // mapping between id and address // TODO: clear mapping once in a while? (for timeout mapping) var _id2addr = {}; - // records for outgoing connections - var _conn = {}; - - // records for sockets of incoming connections - // TODO: simpler approach? (store just _conn and no _sockets?) + // records for connections var _sockets = {}; - - // queue to store messages pending transmission after connection is made + + // queue to store messages pending out-transmission after connection is made var _msgqueue = {}; // net_nodejs object for acting as server (& listen to port) var _server = undefined; // counter for assigning internal / local-only ids - // TODO: this should be removed in future + // TODO: this should be removed in future, or need to re-use id var _id_counter = (-1); // @@ -143,27 +139,30 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // NOTE: mapping stored is from logical (app-layer) id to a host/port pair // *not* mapping from host_id (physical-layer) to host/port pair this.storeMapping = function (id, addr) { + LOG.debug('store mapping for [' + id + ']: ' + addr.toString()); // simply replace any existing mapping _id2addr[id] = addr; } // switch an existing id to socket mapping var l_setID = this.setID = function (new_id, old_id) { - //LOG.debug('replacing old_id [' + old_id + '] with new_id [' + new_id + ']'); // check if setting self id if (old_id === undefined) { + LOG.warn('assigning id to net layer for 1st time: [' + new_id + ']'); _self_id = new_id; } // rename connection ID // NOTE: incoming only, as we should know the id of outgoing connections else if (_sockets.hasOwnProperty(old_id)) { - - _sockets[new_id] = _sockets[old_id]; - delete _sockets[old_id]; - // set new ID for socket (very important for future incoming messages) - _sockets[new_id].id = new_id; + LOG.debug('replacing old_id [' + old_id + '] with new_id [' + new_id + ']'); + + // set new ID for socket (very important for future incoming messages) + _sockets[old_id].id = new_id; + + _sockets[new_id] = _sockets[old_id]; + delete _sockets[old_id]; } else return false; @@ -176,99 +175,96 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { return _self_id; } - // send a message to an id - var l_send = this.send = function (id, msg, is_reliable) { - - // default is reliable TCP transmission - if (is_reliable === undefined) - is_reliable = true; - - // actually sending the message (attaching '\n' to indicate termination) - var send_msg = function (message) { - - LOG.debug('[' + _self_id + '] send_msg to [' + id + ']: '); - LOG.debug(message); + // actually sending the message (attaching '\n' to indicate termination) + // send all messages in pending queue + var l_sendPendingMessages = function (id) { + + // no messages to send + if (_msgqueue.hasOwnProperty(id) === false || _msgqueue[id].length === 0) + return; + + // if target id does not exist, indicate error + if (_sockets.hasOwnProperty(id) === false) { + LOG.error('socket not connected for send target id: ' + id); + return false; + } + + var socket = _sockets[id]; + + var list = _msgqueue[id]; - // if I'm a server, send via one of the recorded sockets - if (_server !== undefined && _sockets.hasOwnProperty(id)) { - //LOG.debug('send to in conn [' + id + ']'); - _server.send(message + '\n', _sockets[id]); - } - // if the target is available on an out-going channel - else if (_conn.hasOwnProperty(id)) { - //LOG.debug('send to out conn [' + id + ']'); - _conn[id].send(message + '\n'); + // send out pending messages + //LOG.debug('[' + _self_id + '] l_sendPendingMessages to [' + id + '], msgsize: ' + list.length); + + for (var i=0; i < list.length; i++) { + + try { + socket.write(list[i] + '\n'); } - // otherwise I should have a connection mapping - else { - LOG.error('no mapping exists for send target id: ' + id); + catch (e) { + LOG.debug('l_sendPendingMessages fail: ' + list[i]); + if (typeof onError === 'function') + onError('send'); return false; - } - - return true; + } } + + // clear queue + _msgqueue[id] = []; + return true; + } - // check if connections exist, if not then start to connect - if (_sockets.hasOwnProperty(id) === true) - return send_msg(msg); - - if (_conn.hasOwnProperty(id) === true) { + // make a connection to a target id + var l_connect = function (id) { + + // check if id to address mapping exist + //LOG.debug('mapping: '); + //LOG.debug(_id2addr); - // for outgoing connections, check if establish, if not then queue - if (_conn[id].isConnected() === true) - return send_msg(msg); - - // store message to queue - LOG.debug('storing msg to msgqueue [' + id + '] msg: ' + msg); - _msgqueue[id].push(msg); - return true; - } - - // check for id to address mapping if (_id2addr.hasOwnProperty(id) === false) { LOG.error('no send target address info for id: ' + id); - return false; + return; } - - // create queue for new socket - _msgqueue[id] = []; - _msgqueue[id].push(msg); - - // create a new socket - _conn[id] = new l_net( + + // create a new out-going connection if not exist + var conn = new l_net( // receive callback _processData, // connect callback + // NOTE: there will be a time gap between send() returns and when connect callbacak is called + // therefore it's possible more messages have been sent to this endpoint before + // the connect event is ever called function (socket) { LOG.debug('[' + id + '] connected'); // store id to socket, so we can identify the source of received messages socket.id = id; - - // notify remote host of my id - // TODO: replace with binary handshake (for efficiency) - //_conn[id].send(_self_id + '\n'); - // DEBUG: when connections are made quickly, it's possible at this stage - // _conn[id] is not yet initialized so need to write directly - // however, calling 'write' here does not look clean - socket.write(_self_id + '\n'); + // store to socket list + _sockets[id] = socket; + if (id < 0) { + LOG.error('connected socket id < 0: ' + id); + } + // notify connection - if (typeof CB_connect === 'function') - CB_connect(id); - - // send out pending messages - if (_msgqueue.hasOwnProperty(id)) { - var list = _msgqueue[id]; - LOG.debug('msgqueue size for [' + id + ']: ' + list.length); - for (var i=0; i < list.length; i++) - send_msg(list[i]); - - // clear queue + if (typeof onConnect === 'function') + onConnect(id); + + // create queue for new socket if not exist + // TODO: investigate why this happens (msgqueue should've already been init before this) + // unless, a disconnect for this 'id' has occured + // (so that means, before a new connection is made, another connection with the same remote host + // has been disconnected) + if (_msgqueue.hasOwnProperty(id) === false) { + var mq_size = Object.keys(_msgqueue).length; + var s_size = Object.keys(_sockets).length; + LOG.error('[' + _self_id + '] msgqueue has no target [' + id + ']. should not happen, mq: ' + mq_size + ' sock: ' + s_size); _msgqueue[id] = []; - } + } + + l_sendPendingMessages(id); }, // disconnect callback @@ -278,9 +274,15 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // remove id delete socket.id; + // remove from socket list + delete _sockets[id]; + + // remove all pending messages + delete _msgqueue[id]; + // notify disconnection - if (typeof CB_disconnect === 'function') - CB_disconnect(id); + if (typeof onDisconnect === 'function') + onDisconnect(id); }, // error callback @@ -290,32 +292,102 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { ); // make connection - _conn[id].connect(_id2addr[id]); + // NOTE: send will return immediately, so the actual send may not occur until later + // it's possible that after returning, upper layer starts to send messages + conn.connect(_id2addr[id]); return true; + + } + + // send a message to an id + var l_send = this.send = function (id_list, pack, is_reliable, onDone) { + + // default is reliable TCP transmission + is_reliable = is_reliable || true; + + // attach sender id to message + pack.src = _self_id; + + // serialize string + var encode_str = JSON.stringify(pack); + + var target_list = ''; + + // go through each target id to send + for (var i=0; i < id_list.length; i++) { + var id = id_list[i]; + + // check if there's existing connection, or id to address mapping + target_list += (id + ','); + + // check if it's a self message + if (id === _self_id) { + + LOG.warn('send message to self [' + _self_id + ']'); + // pass message to upper layer for handling + if (typeof onReceive === 'function') { + onReceive(_self_id, pack); + } + continue; + } + + // store message to queue + // create queue for connection if not exist + if (_msgqueue.hasOwnProperty(id) === false) + _msgqueue[id] = []; + + _msgqueue[id].push(encode_str); + + // if connections already exists, send directly, otherwise establish new connection + if (_sockets.hasOwnProperty(id)) + l_sendPendingMessages(id); + else { + var str = ''; + var unknown_count = 0; + for (var s in _sockets) { + str += (_sockets[s].id + ' '); + if (_sockets[s].id < 0) + unknown_count++; + } + + var sock_size = Object.keys(_sockets).length; + if (unknown_count > 5) { + LOG.warn('[' + _self_id + '] attempts to connect to [' + id + '] sock_size: ' + sock_size); + //LOG.warn(str); + } + l_connect(id); + } + } + + LOG.debug('[' + _self_id + '] SEND to [' + target_list + ']: ' + encode_str); } // start a server at a given port - this.listen = function (port, CB_done) { + this.listen = function (port, onDone) { if (port === undefined || port < 0 || port >= 65536) { LOG.error('port not provided, cannot start listening'); - if (typeof CB_done === 'function') - CB_done(0); + if (typeof onDone === 'function') + onDone(0); return; } // open server _server = new l_net( // receive callback - _processData, + _processData, // connect callback function (socket) { + + //LOG.warn('new socket connected, id: ' + socket.id); // check if id doesn't exist, indicate it's unassigned - if (typeof socket.id === 'undefined') - socket.id = _id_counter--; + if (socket.hasOwnProperty(id) === false) { + socket.id = _id_counter--; + //LOG.warn('assigning id: ' + socket.id); + } // record this socket // NOTE: this is an important step, otherwise a server will not be @@ -323,8 +395,8 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { _sockets[socket.id] = socket; // notify connection - if (typeof CB_connect === 'function') - CB_connect(socket.id); + if (typeof onConnect === 'function') + onConnect(socket.id); }, // disconnect callback @@ -332,8 +404,8 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { //console.log('disconnet occur'); // notify disconnection - if (typeof CB_disconnect === 'function') - CB_disconnect(socket.id); + if (typeof onDisconnect === 'function') + onDisconnect(socket.id); }, // error callback @@ -347,10 +419,10 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // check for success if (port_binded != 0) { - //LOG.debug('port bind successful: ' + port_binded); + LOG.warn('port bind successful: ' + port_binded); // return the actual port binded - if (typeof CB_done === 'function') - CB_done(port_binded); + if (typeof onDone === 'function') + onDone(port_binded); return; } @@ -379,23 +451,16 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // remove an existing connection this.disconnect = function (id) { - // if id belongs to an incoming socket if (_sockets.hasOwnProperty(id)) { - LOG.debug('disconnect incoming socket for id: ' + id); - _sockets[id].end(); + LOG.debug('disconnect conn id: ' + id); + _sockets[id].disconnect(); delete _sockets[id]; - } - else if (_conn.hasOwnProperty(id)) { - LOG.debug('disconnect outgoing socket for id: ' + id); - _conn[id].disconnect(); - delete _conn[id]; delete _msgqueue[id]; } else { LOG.debug('cannot find id [' + id + '] to disconnect'); return false; } - return true; } @@ -403,37 +468,20 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { // // private methods // - - // new ID assignment - var _new_ID = undefined; - var _assignNewID = function (socket) { - - // we use our own ID as first - // NOTE if we start with VAST_ID_UNASSIGNED (0) then first ID will be 1 - if (_new_ID === undefined) - _new_ID = _self_id + 1; - - LOG.debug('new ID assigned for socket: ' + socket.host + ':' + socket.port); - return _new_ID++; - } var _processData = function (socket, data) { - //console.log('vast_net id: ' + _self_id + ' processData: ' + data); + //LOG.warn('processData, socket.id: ' + socket.id); // create buffer for partially received message, if not exist - if (typeof socket.recv_buf === 'undefined') { - //console.log('init recv_buf'); + if (typeof socket.recv_buf === 'undefined') socket.recv_buf = ''; - } - + // store data to buffer directly first socket.recv_buf += data; // start loop of checking for complete messages while (true) { - - //console.log('recv_buf: ' + socket.recv_buf); // check if it's a complete message (termined with '\n') var idx = socket.recv_buf.search('\n'); @@ -442,68 +490,76 @@ function vast_net(CB_receive, CB_connect, CB_disconnect, id) { if (idx === (-1)) break; - var str = socket.recv_buf.slice(0, idx); - + // get new message and update buffer + var str = socket.recv_buf.slice(0, idx); + socket.recv_buf = socket.recv_buf.substr(idx + 1); + // deliver this parsed data for processing - if (str !== undefined) { - //LOG.debug('str: ' + str + ' from id: ' + socket.id); - - // check if remote connection sends in its ID initially - // NOTE: this check should be performed only once - // NOTE: as this check is before receiving new ID from remote host, - // the first node joining the system will be given the gateway's ID (1) - if (socket.id < VAST_ID_UNASSIGNED) { - - // assume the first message is remote node's id - var remote_id = parseInt(str); - LOG.debug('remote id: ' + remote_id); - - // skip message if remote_id is invalid - // TODO: try to determine the cause & fix this - // NOTE: if this happens, the message is simply ignored - if (isNaN(remote_id)) { - LOG.error('[' + _self_id + '] remote_id is NaN, str: ' + str + ' from socket id: ' + socket.id); - } - else { - - // if remote id is not yet assigned, assign new one - if (remote_id === VAST_ID_UNASSIGNED) { - remote_id = _assignNewID(socket); - - // check if this is the first ever ID assigned by me - // if so, then I'm likely the gateway (my ID is also unassigned yet) - if (_self_id === VAST_ID_UNASSIGNED) { - LOG.warn('first ID assigned, likely I am the gateway'); - _self_id = remote_id; - } - // notify remote node of its new ID - else { - LOG.debug('assign new ID [' + remote_id + '] to [' + socket.id + ']'); - l_send(socket.id, remote_id); - } - } - - l_setID(remote_id, socket.id); - } - } - // check if the message is a new ID assignment for me - // NOTE: we assume the first message received is new id - else if (_self_id === VAST_ID_UNASSIGNED) { - - var assigned_id = parseInt(str); - LOG.debug('assigned_id: ' + assigned_id); - _self_id = assigned_id; - } - // otherwise is a real msg, notify custom callback of incoming data (if provided) - else if (typeof CB_receive === 'function') { - //LOG.debug('recv from [' + socket.id + '] str: ' + str); + if (str === undefined) { + LOG.error('str is undefined, recv_buf: ' + socket.recv_buf + ' from id: ' + socket.id); + continue; + } + + // unpack packet + var pack; + + try { + // convert msg back to js_obj + pack = JSON.parse(str); + } + catch (e) { + LOG.error('msgHandler: convert to js_obj fail: ' + e + ' str: ' + str); + continue; + } + + // if pack is invalid + if (pack.hasOwnProperty('src') === false || + pack.hasOwnProperty('type') === false || + pack.hasOwnProperty('msg') === false) { + LOG.error('[' + _self_id + '] invalid packet to process: ' + str); + continue; + } + + LOG.debug('RECV from [' + pack.src + ']: ' + str); + + if (socket.hasOwnProperty('id') === false) { + LOG.warn('socket has not id assigned yet...assigning: ' + pack.src); + socket.id = parseInt(pack.src); + } + + var remote_id = socket.id; + + // check if remote connection sends in its ID initially + // NOTE: this check should be performed only once + // NOTE: as this check is before receiving new ID from remote host, + // the first node joining the system will be given the gateway's ID (1) + if (remote_id < VAST_ID_UNASSIGNED) { + + var sender_id = parseInt(pack.src); + + LOG.debug('[' + _self_id + '] learns sender id: ' + sender_id); + + // if ID exists, then there's already an established connection + if (_sockets.hasOwnProperty(sender_id) === true) { + LOG.warn('[' + _self_id + '] redundent socket already exists: ' + sender_id); - // TODO: queue-up message to be processed later? - CB_receive(socket.id, str); + // disconnect remote host + // however, message still needs to deliver + //socket.end(); } - } - // update buffer - socket.recv_buf = socket.recv_buf.substr(idx + 1); + + // store the remote ID as remote host's socket ID + else if (sender_id !== VAST_ID_UNASSIGNED) { + l_setID(sender_id, socket.id); + remote_id = sender_id; + } + } + + // pass message to upper layer for handling + if (typeof onReceive === 'function') { + // TODO: queue-up message to be processed later? + onReceive(remote_id, pack); + } } }; diff --git a/test_VAST_client.js b/test_VAST_client.js index 276a9bcc..282dff30 100644 --- a/test_VAST_client.js +++ b/test_VAST_client.js @@ -16,21 +16,31 @@ require('./common'); //LOG.setLevel(2); // set default IP/port -var ip_port = {host: "127.0.0.1", port: 37700}; - -// get custom parameters -var port = process.argv[2]; -if (port !== undefined) - ip_port.port = port; - -var host = process.argv[3]; +// set default IP/port +var gateway_addr = {host: "127.0.0.1", port: 37700}; var is_client = false; -if (host !== undefined) { - ip_port.host = host; - is_client = true; + +// IP/port +if (process.argv[2] !== undefined) { + var ip_port = process.argv[2]; + // check if this is port only + var idx = ip_port.search(':'); + + // ':' not found, port only + if (idx === (-1)) + gateway_addr.port = parseInt(ip_port); + else { + var ip = ip_port.slice(0, idx); + var port = ip_port.slice(idx+1, ip_port.length); + gateway_addr.host = ip; + gateway_addr.port = parseInt(port); + + is_client = true; + } } -LOG.debug('host: ' + ip_port.host + ' port: ' + ip_port.port + ' is_client: ' + is_client); +LOG.debug('GW ip: ' + gateway_addr.host + ' port: ' + gateway_addr.port); +LOG.debug('is_client: ' + is_client); var x = Math.floor(Math.random() * 100); var y = Math.floor(Math.random() * 100); @@ -58,11 +68,11 @@ var moveAround = function () { var interval_id = undefined; // after init the client will bind to a local port -client.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), ip_port.port, function () { +client.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.port, function () { LOG.warn('test_VAST_client: init done'); - client.join(ip_port, + client.join(gateway_addr, // done callback function (id) { diff --git a/test_VON_peer.js b/test_VON_peer.js index e6c26a24..b13af243 100644 --- a/test_VON_peer.js +++ b/test_VON_peer.js @@ -10,11 +10,8 @@ var AUTOMATIC_LEAVE_PERIOD = 3; // number of seconds require('./common'); -//var curr = new Date(); -//LOG.debug(curr.toLocaleString()); - // do not show debug -//LOG.setLevel(2); +LOG.setLevel(2); // set default IP/port var gateway_addr = {host: "127.0.0.1", port: 37700}; @@ -54,8 +51,14 @@ var moveAround = function () { // move if not GW if (peer.getSelf().id !== VAST_ID_GATEWAY) { // random walk new location (5 units within current center position) - aoi.center.x += Math.floor((Math.random()%10 - 5)); - aoi.center.y += Math.floor((Math.random()%10 - 5)); + aoi.center.x += Math.floor((Math.random()%10) - 5); + aoi.center.y += Math.floor((Math.random()%10) - 5); + + if (aoi.center.x < 0) + aoi.center.x *= -1; + if (aoi.center.y < 0) + aoi.center.y *= -1; + } var neighbor_size = Object.keys(peer.list()).length; @@ -68,9 +71,9 @@ var moveAround = function () { var interval_id = undefined; // after init the peer will bind to a local port -peer.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.port, gateway_addr, function () { +peer.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.port, function () { - peer.join(aoi, + peer.join(gateway_addr, aoi, // done callback function (id) { diff --git a/test_VON_scale.js b/test_VON_scale.js index c5e6a01d..8ccacf82 100644 --- a/test_VON_scale.js +++ b/test_VON_scale.js @@ -1,3 +1,4 @@ + /* test for many VON_peer @@ -56,22 +57,22 @@ var VONnode = function (num, GWaddr, radius) { // create GW or a connecting client; var peer = new VON.peer(); - var aoi = new VAST.area(new VAST.pos(pos.x, pos.y), radius); + var aoi = new VAST.area(new VAST.pos(Math.floor(pos.x), Math.floor(pos.y)), radius); // perform movement var moveNode = function () { var new_pos = movement.getpos(num-1); - aoi.center.x = new_pos.x; - aoi.center.y = new_pos.y; + aoi.center.x = Math.floor(new_pos.x); + aoi.center.y = Math.floor(new_pos.y); LOG.debug('node num: ' + num + ' moves to ' + aoi.center); peer.move(aoi); } - peer.init(VAST_ID_UNASSIGNED, port, GWaddr, function () { + peer.init(VAST_ID_UNASSIGNED, port, function () { - peer.join(aoi, + peer.join(GWaddr, aoi, // done callback function (id) { @@ -102,7 +103,7 @@ var createNode = function () { // see if we want to create more if (nodes_created < node_size) - setTimeout(createNode, 500); + setTimeout(createNode, 1000); } LOG.debug('creating ' + node_size + ' nodes @ host: ' + gateway_addr.host + ' port: ' + gateway_addr.port); diff --git a/vast.io/vast.io-client.html b/vast.io/vast.io-client.html index 01630a52..6a3154ea 100644 --- a/vast.io/vast.io-client.html +++ b/vast.io/vast.io-client.html @@ -23,8 +23,8 @@ var dy = 5; //var addr = {host: '127.0.0.1', port: 38800}; - //var addr = {host: 'prod.imonology.com', port: 38800}; - var addr = {host: 'dev.imonology.com', port: 38800}; + var addr = {host: 'api.gaiasup.com', port: 38800}; + //var addr = {host: 'dev.imonology.com', port: 38800}; var aoi = {center: {x: self_x, y: self_y}, radius: self_radius}; var self_id = undefined; diff --git a/vast.io/vast.io-server.js b/vast.io/vast.io-server.js index 3e8fb577..bb24170d 100644 --- a/vast.io/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -85,9 +85,9 @@ io.sockets.on('connection', function (socket) { nodes_created++; // join in the network - _self.init(VAST_ID_UNASSIGNED, ip_port.port + nodes_created, ip_port, function () { + _self.init(VAST_ID_UNASSIGNED, ip_port.port + nodes_created, function () { - _self.join(aoi, + _self.join(ip_port, aoi, // done callback function (self_id) { diff --git a/vast_types.js b/vast_types.js index 8657f862..3d37a730 100644 --- a/vast_types.js +++ b/vast_types.js @@ -185,7 +185,7 @@ var l_endpt = exports.endpt = function (host, port) { this.addr = new l_addr(host, port); this.toString = function () { - return 'Endpoint: host [' + this.host_id + '] ' + this.addr.toString(); + return 'host [' + this.host_id + '] ' + this.addr.toString(); } // update info from existing object @@ -259,23 +259,25 @@ var l_node = exports.node = function (id, endpt, aoi, time) { // this.meta = js_obj.meta; } catch (e) { - console.log('node parse error: ' + e); + console.log('node parse error: ' + e.stack); } } // print out node info this.toString = function () { //return '[' + this.id + '] ' + this.endpt.toString() + ' ' + this.aoi.toString() + ' meta: ' + Object.keys(this.meta).length; - return '[' + this.id + '] ' + this.endpt.toString() + ' ' + this.aoi.toString(); + return '[' + this.id + ']: ' + this.endpt.toString() + ' ' + this.aoi.toString(); } } -// definition for a node -// 'area': a vast_area object -// 'addr': a vast_addr object -var l_pack = exports.pack = function (type, msg, priority) { - +// definition for a network packet +// type: number message type +// msg: string actual message +// priority: number sending priority +// sender: string sender id +var l_pack = exports.pack = function (type, msg, priority, sender) { + // the message type this.type = type; @@ -287,6 +289,9 @@ var l_pack = exports.pack = function (type, msg, priority) { // target is a list of node IDs this.targets = []; + + // sender id + this.src = sender || 0; } From 3cf2a8f1ab0fc8987b596477d5ece9221bea104e Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 5 Mar 2013 21:24:18 +0800 Subject: [PATCH 020/141] re-structure directory to be integrated with ImonCloud --- VAST.js | 406 +++--------------- VAST_client.js | 354 +++++++++++++++ VAST_matcher.js | 2 +- VON_peer.js | 2 +- VSO_peer.js | 2 +- SFVoronoi.java => VSS/SFVoronoi.java | 0 line2d.java => VSS/line2d.java | 0 common.js | 60 --- net/net_nodejs.js | 2 +- server.js | 18 - .../test_VAST_client.js | 2 +- test_VON_peer.html => test/test_VON_peer.html | 0 test_VON_peer.js => test/test_VON_peer.js | 2 +- test_VON_scale.js => test/test_VON_scale.js | 4 +- test_fresh.js => test/test_fresh.js | 0 test_inhert.js => test/test_inhert.js | 0 test_net.js => test/test_net.js | 0 test_net_nodejs.js => test/test_net_nodejs.js | 0 test_voro.html => test/test_voro.html | 0 test_voro.js => test/test_voro.js | 0 .../test_voro_rh (interactive).html | 0 .../test_voro_rh (specify points).html | 0 test_voro_rh.html => test/test_voro_rh.html | 0 test_voro_sf.html => test/test_voro_sf.html | 0 24 files changed, 418 insertions(+), 436 deletions(-) create mode 100644 VAST_client.js rename SFVoronoi.java => VSS/SFVoronoi.java (100%) rename line2d.java => VSS/line2d.java (100%) delete mode 100644 common.js delete mode 100644 server.js rename test_VAST_client.js => test/test_VAST_client.js (99%) rename test_VON_peer.html => test/test_VON_peer.html (100%) rename test_VON_peer.js => test/test_VON_peer.js (99%) rename test_VON_scale.js => test/test_VON_scale.js (97%) rename test_fresh.js => test/test_fresh.js (100%) rename test_inhert.js => test/test_inhert.js (100%) rename test_net.js => test/test_net.js (100%) rename test_net_nodejs.js => test/test_net_nodejs.js (100%) rename test_voro.html => test/test_voro.html (100%) rename test_voro.js => test/test_voro.js (100%) rename test_voro_rh (interactive).html => test/test_voro_rh (interactive).html (100%) rename test_voro_rh (specify points).html => test/test_voro_rh (specify points).html (100%) rename test_voro_rh.html => test/test_voro_rh.html (100%) rename test_voro_sf.html => test/test_voro_sf.html (100%) diff --git a/VAST.js b/VAST.js index a5e9258c..edd7bf23 100644 --- a/VAST.js +++ b/VAST.js @@ -1,354 +1,60 @@ /* - * VAST, a scalable peer-to-peer network for virtual environments - * Copyright (C) 2005-2011 Shun-Yun Hu (syhu@ieee.org) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/* - The basic interface for all major VAST functions. - supporting spatial publish subscribe (SPS) - - supported functions: - - // basic callback / structure - pos = {x, y} - addr = {host, port} - area = {pos, radius} - msg = {from_id, size, type, data, is_reliable} // message sent & received by clients - sub = {sub_id, subscriber, layer, aoi, relay} // subscription info - - sub_CB(result, subID) // callback to receive subscribe result (success/fail) - neighbor_CB(list) // callback to receive neighbor (node) list - recv_CB(msg) // callback to receive any messages - - // constructor - VAST(recv_CB, GW_addr) - - // basic functions - subscribe(layer, area, sub_CB) subscribe for an area at a given layer - publish(layer, area, msg) publish a message to an area at a given layer - move(subID, area) move a subscription to a new position - send(id, msg) send a message to specified user(s) - list(area, neighbor_CB) get a list of subscribers within an area - - // stat / accessors - getPhysicalNeighbors() get a list of physical neighbors currently known - getLogicalNeighbors() get a list of logical neighbors currently known - reportGateway(msg) send some custom message to gateway (for stat / record keeping) - reportOrigin(msg) send some custom message to origin matcher (for stat / record keeping) - getStat() get stat stored locally - getSelf() get info on self node - getSubID() get my current subscription ID (should return multiple?) - getWorldID() get my world ID - getLatency(msgtype) get latency stat for a particular message type - - // state report - isJoined() check if I'm joined - isRelay() check if I'm a Relay node - isPublic() check if I have public address (IP) so may serve - - history: - 2012-07-06 initial version (convert interface from VAST.h) - 2012-09-10 define interface & begin implementation + common definitions for VAST.js */ - -require('./common.js'); - -var msg_handler = msg_handler || require('./net/msg_handler.js'); - -// config -var VAST_DEFAULT_PORT = 37; // by default which port does the node listen -var TIMEOUT_SUBSCRIBE = (5); // number of seconds before re-attempting to subscribe - -//var TIMEOUT_REMOVE_GHOST = (5); // # of seconds before removing ghost objects at clients - -function VAST_client(recv_callback, GW_addr) { - - // callback to notify subscribed messages received - var _recv_CB = recv_callback; - - // info about the gateway server - var _gateway = GW_addr || "127.0.0.1:37700"; - - // state of joining - var _state = VAST.state.ABSENT; - - // my subscription record - var _sub = VAST.sub(); - - // callback to notify when subscribe is done - var _sub_CB = undefined; - - // information regarding current node - var _self; - - // id for owner matcher, default to gateway - var _matcher_id = VAST_ID_GATEWAY; - -/* - // variables used by VASTClient component - - vector _neighbors; // list of current AOI neighbors - vector _physicals; // list of physical neighbors - vector _logicals; // list of logical neighbors - - id_t _closest_id; // hostID for the closest neighbor matcher - VASTRelay *_relay; // pointer to VASTRelay (to obtain relayID) - - // timeouts - timestamp_t _next_periodic; // next timestamp to perform tasks - timestamp_t _timeout_subscribe; // timeout for re-attempt to subscribe - map _last_update; // last update time for a particular neighbor - - - // storage for incoming messages - vector _msglist; // record for incoming messages - VASTBuffer _recv_buf; // a receive buffer for incoming messages - Message * _lastmsg; // last message received from network (to be deleted) - // TODO: a better way for it? - - // stats - map _latency; // latencies for different message types -*/ - - // - // public methods - // - - // subscribe for an area at a given layer - this.subscribe = function (layer, area, sub_CB) { - - // record callback when subscribe is done - _sub_CB = sub_CB; - - // store host to which matching publication should be sent (back to myself) - _sub.host_id = _getID(); - - //_sub.host_id = _net->getHostID (); - //_sub.active = false; - //_sub.relay = _net->getAddress (_relay->getRelayID ()); - - // record my subscription, not yet successfully subscribed - // NOTE: currently we assume we subscribe only one at a time - // also, it's important to record the area & layer first, so that - // re-subscribe attempt may be correct (in case the check below is not passed yet) - - // NOTE: because my hostID is unique, it guarantee my subscription ID is also unique - if (_sub.id === VAST_ID_UNASSIGNED) - _sub.id = _getID(); - - _sub.aoi = area; - _sub.layer = layer; - - // activate re-try mechanism - // NOTE: is this such a good idea (will result in request flooding?) - _subscribeRetry(); - - /* - // if matcher or relay is not yet known, wait first - if (_state != JOINED || _relay->isJoined () == false) - { - LogManager::instance ()->writeLogFile ("VASTClient::subscribe () [%llu] matcher or relay not ready, wait first. matcher: [%llu], relay joined: %s\n", _self.id, _matcher_id, (_relay->isJoined () ? "true" : "false")); - return false; - } - */ - - } - - // publish a message to an area at a given layer - this.publish = function (layer, area, msg) { - - } - - // move a subscription to a new position - this.move = function (subID, area) { - } - - // send a message to specified user(s) - this.send = function (id, msg) { - } - - // get a list of subscribers within an area - this.list = function (area, neighbor_CB) { - } - - // - // private methods - // - - var _subscribeRetry = function () { - - // no need to re-try if subscription is completed - if (_sub_CB === undefined) - return; - - LOG.debug('[' + _self.id + '] sends SUBSCRIBE request to [' + _matcher_id + ']'); - - // set timeout to re-try, necessary because it takes time to send the subscription, which can be lost - setTimeout(_subscribeRetry, 1000 * TIMEOUT_SUBSCRIBE); - - // send out subscription request to owner matcher - var pack = new VAST.pack(VAST.msgtype.SUB, _sub, VAST.priority.HIGH); - _sendMatcherMessage(pack); - } - - // send to gateway a message on a given handler - var _sendGatewayMessage = function (pack) { - - pack.targets = []; - pack.targets.push(VAST_ID_GATEWAY); - _sendPack(pack, true); - } - - // send to gateway a message on a given handler - var _sendMatcherMessage = function (pack) { - - pack.targets = []; - pack.targets.push(_matcher_id); - _sendPack(pack, true); - } - - // - // handlers to communicate with network layer - // - - var _packetHandler = this.packetHandler = function (from_id, pack) { - - // if join is not even initiated, do not process any message - if (_state == VAST.state.ABSENT) { - LOG.error('node not yet join, should not process any messages'); - return false; - } - - LOG.debug('VAST Client [' + _self.id + '] ' + VAST.msgstr[pack.type] + ' from [' + from_id + ']'); - - switch (pack.type) { - - // VON's query, to find an acceptor that can take in a joining node - case VAST.msgtype.VAST_SOMETHING: { - } - break; - - default: - // packet unhandled - LOG.debug('VAST Client: message unprocessed'); - return false; - break; - } - - // successfully handle packet - return true; - } - - var _connHandler = this.connHandler = function (id) { - LOG.debug('VAST Client [' + id + '] connected'); - } - - var _disconnHandler = this.disconnHandler = function (id) { - LOG.debug('VAST Client [' + id + '] disconnected'); - - // generate a BYE message - var pack = new VAST.pack( - VAST.msgtype.BYE, - {}, - VAST.priority.HIGHEST); - - _packetHandler(id, pack); - } - - ///////////////////// - // msg_handler methods - // - - var _that = this; - - // function to create a new net layer - this.init = function (self_id, port, done_CB) { - - self_id = self_id || VAST_ID_UNASSIGNED; - port = port || VAST_DEFAULT_PORT; - - // create message handler manager and add self as one of the handlers - var handler = new msg_handler(self_id, port, function (local_addr) { - - // NOTE: this will cause initStates() be called - handler.addHandler(_that); - - // notify done - if (typeof done_CB === 'function') - done_CB(local_addr); - }); - } - - var _initStates = this.initStates = function (msg_handler) { - - if (msg_handler !== undefined) { - - _msg_handler = msg_handler; - - var id = _msg_handler.getID(); - LOG.warn('VAST_client initStates called with msg_handler, id: ' + id); - - // add convenience references - _storeMapping = _msg_handler.storeMapping, - _getID = _msg_handler.getID, - _disconnect = _msg_handler.disconnect, - _sendMessage = _msg_handler.sendMessage, - _sendPack = _msg_handler.sendPack; - - // add matcher as handler - var matcher = new VAST.matcher(); - _msg_handler.addHandler(matcher); - - // create a self node - _self = new VAST.node(_getID()); - - // TODO: move this somewhere? so the above can be extracted as standard - // do constructor work - - // ensure gateway's type is correct - // TODO: validate gateway address - var addr = new VAST.addr(); - addr.parse(_gateway); - _gateway = addr; - - // store gateway address - _storeMapping(VAST_ID_GATEWAY, _gateway); - } - } - - ///////////////////// - // constructor - // - LOG.debug('VAST constructor called'); - - // - // connect with handler - // - - var _msg_handler; - - // convenience references - var _storeMapping, _getID, _disconnect, _sendMessage, _sendPack; - -} - -// export the class with conditional check -if (typeof module !== "undefined") - module.exports = VAST_client; +// +// utilities +// + +var logger = require('./common/logger'); +global.LOG = new logger(); +global.UTIL = require('./common/util'); + +// set default error level +LOG.setLevel(3); + +// +// VAST & VON +// +// ID definitions +global.VAST_ID_UNASSIGNED = 0; +global.VAST_ID_GATEWAY = 1; + +global.VAST = require('./vast_types'); +global.VAST.net = require('./net/vast_net'); +global.VAST.client = require('./VAST'); +global.VAST.matcher = require('./VAST_matcher'); + +// TODO: find a better way to store this? (maybe in msg_handler?) +global.VAST.state = { + ABSENT: 0, + // INIT: 1, // init done + JOINING: 2, // different stages of join + JOINED: 3 +}; + +global.VAST.priority = { + HIGHEST: 0, + HIGH: 1, + NORMAL: 2, + LOW: 3, + LOWEST: 4 +}; + +// TODO: combine into nicer-looking global +global.VAST.msgtype = { + BYE: 0, // disconnect + PUB: 1, // publish request + SUB: 2 // subscribe request +}; + +global.VAST.msgstr = [ + 'BYE', + 'PUB', + 'SUB' +]; + +global.VON = { + peer: require('./VON_peer') +}; diff --git a/VAST_client.js b/VAST_client.js new file mode 100644 index 00000000..d491c774 --- /dev/null +++ b/VAST_client.js @@ -0,0 +1,354 @@ + +/* + * VAST, a scalable peer-to-peer network for virtual environments + * Copyright (C) 2005-2011 Shun-Yun Hu (syhu@ieee.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + The basic interface for all major VAST functions. + supporting spatial publish subscribe (SPS) + + supported functions: + + // basic callback / structure + pos = {x, y} + addr = {host, port} + area = {pos, radius} + msg = {from_id, size, type, data, is_reliable} // message sent & received by clients + sub = {sub_id, subscriber, layer, aoi, relay} // subscription info + + sub_CB(result, subID) // callback to receive subscribe result (success/fail) + neighbor_CB(list) // callback to receive neighbor (node) list + recv_CB(msg) // callback to receive any messages + + // constructor + VAST(recv_CB, GW_addr) + + // basic functions + subscribe(layer, area, sub_CB) subscribe for an area at a given layer + publish(layer, area, msg) publish a message to an area at a given layer + move(subID, area) move a subscription to a new position + send(id, msg) send a message to specified user(s) + list(area, neighbor_CB) get a list of subscribers within an area + + // stat / accessors + getPhysicalNeighbors() get a list of physical neighbors currently known + getLogicalNeighbors() get a list of logical neighbors currently known + reportGateway(msg) send some custom message to gateway (for stat / record keeping) + reportOrigin(msg) send some custom message to origin matcher (for stat / record keeping) + getStat() get stat stored locally + getSelf() get info on self node + getSubID() get my current subscription ID (should return multiple?) + getWorldID() get my world ID + getLatency(msgtype) get latency stat for a particular message type + + // state report + isJoined() check if I'm joined + isRelay() check if I'm a Relay node + isPublic() check if I have public address (IP) so may serve + + history: + 2012-07-06 initial version (convert interface from VAST.h) + 2012-09-10 define interface & begin implementation +*/ + + +require('./common.js'); + +var msg_handler = msg_handler || require('./net/msg_handler.js'); + +// config +var VAST_DEFAULT_PORT = 37; // by default which port does the node listen +var TIMEOUT_SUBSCRIBE = (5); // number of seconds before re-attempting to subscribe + +//var TIMEOUT_REMOVE_GHOST = (5); // # of seconds before removing ghost objects at clients + +function VAST_client(recv_callback, GW_addr) { + + // callback to notify subscribed messages received + var _recv_CB = recv_callback; + + // info about the gateway server + var _gateway = GW_addr || "127.0.0.1:37700"; + + // state of joining + var _state = VAST.state.ABSENT; + + // my subscription record + var _sub = VAST.sub(); + + // callback to notify when subscribe is done + var _sub_CB = undefined; + + // information regarding current node + var _self; + + // id for owner matcher, default to gateway + var _matcher_id = VAST_ID_GATEWAY; + +/* + // variables used by VASTClient component + + vector _neighbors; // list of current AOI neighbors + vector _physicals; // list of physical neighbors + vector _logicals; // list of logical neighbors + + id_t _closest_id; // hostID for the closest neighbor matcher + VASTRelay *_relay; // pointer to VASTRelay (to obtain relayID) + + // timeouts + timestamp_t _next_periodic; // next timestamp to perform tasks + timestamp_t _timeout_subscribe; // timeout for re-attempt to subscribe + map _last_update; // last update time for a particular neighbor + + + // storage for incoming messages + vector _msglist; // record for incoming messages + VASTBuffer _recv_buf; // a receive buffer for incoming messages + Message * _lastmsg; // last message received from network (to be deleted) + // TODO: a better way for it? + + // stats + map _latency; // latencies for different message types +*/ + + // + // public methods + // + + // subscribe for an area at a given layer + this.subscribe = function (layer, area, sub_CB) { + + // record callback when subscribe is done + _sub_CB = sub_CB; + + // store host to which matching publication should be sent (back to myself) + _sub.host_id = _getID(); + + //_sub.host_id = _net->getHostID (); + //_sub.active = false; + //_sub.relay = _net->getAddress (_relay->getRelayID ()); + + // record my subscription, not yet successfully subscribed + // NOTE: currently we assume we subscribe only one at a time + // also, it's important to record the area & layer first, so that + // re-subscribe attempt may be correct (in case the check below is not passed yet) + + // NOTE: because my hostID is unique, it guarantee my subscription ID is also unique + if (_sub.id === VAST_ID_UNASSIGNED) + _sub.id = _getID(); + + _sub.aoi = area; + _sub.layer = layer; + + // activate re-try mechanism + // NOTE: is this such a good idea (will result in request flooding?) + _subscribeRetry(); + + /* + // if matcher or relay is not yet known, wait first + if (_state != JOINED || _relay->isJoined () == false) + { + LogManager::instance ()->writeLogFile ("VASTClient::subscribe () [%llu] matcher or relay not ready, wait first. matcher: [%llu], relay joined: %s\n", _self.id, _matcher_id, (_relay->isJoined () ? "true" : "false")); + return false; + } + */ + + } + + // publish a message to an area at a given layer + this.publish = function (layer, area, msg) { + + } + + // move a subscription to a new position + this.move = function (subID, area) { + } + + // send a message to specified user(s) + this.send = function (id, msg) { + } + + // get a list of subscribers within an area + this.list = function (area, neighbor_CB) { + } + + // + // private methods + // + + var _subscribeRetry = function () { + + // no need to re-try if subscription is completed + if (_sub_CB === undefined) + return; + + LOG.debug('[' + _self.id + '] sends SUBSCRIBE request to [' + _matcher_id + ']'); + + // set timeout to re-try, necessary because it takes time to send the subscription, which can be lost + setTimeout(_subscribeRetry, 1000 * TIMEOUT_SUBSCRIBE); + + // send out subscription request to owner matcher + var pack = new VAST.pack(VAST.msgtype.SUB, _sub, VAST.priority.HIGH); + _sendMatcherMessage(pack); + } + + // send to gateway a message on a given handler + var _sendGatewayMessage = function (pack) { + + pack.targets = []; + pack.targets.push(VAST_ID_GATEWAY); + _sendPack(pack, true); + } + + // send to gateway a message on a given handler + var _sendMatcherMessage = function (pack) { + + pack.targets = []; + pack.targets.push(_matcher_id); + _sendPack(pack, true); + } + + // + // handlers to communicate with network layer + // + + var _packetHandler = this.packetHandler = function (from_id, pack) { + + // if join is not even initiated, do not process any message + if (_state == VAST.state.ABSENT) { + LOG.error('node not yet join, should not process any messages'); + return false; + } + + LOG.debug('VAST Client [' + _self.id + '] ' + VAST.msgstr[pack.type] + ' from [' + from_id + ']'); + + switch (pack.type) { + + // VON's query, to find an acceptor that can take in a joining node + case VAST.msgtype.VAST_SOMETHING: { + } + break; + + default: + // packet unhandled + LOG.debug('VAST Client: message unprocessed'); + return false; + break; + } + + // successfully handle packet + return true; + } + + var _connHandler = this.connHandler = function (id) { + LOG.debug('VAST Client [' + id + '] connected'); + } + + var _disconnHandler = this.disconnHandler = function (id) { + LOG.debug('VAST Client [' + id + '] disconnected'); + + // generate a BYE message + var pack = new VAST.pack( + VAST.msgtype.BYE, + {}, + VAST.priority.HIGHEST); + + _packetHandler(id, pack); + } + + ///////////////////// + // msg_handler methods + // + + var _that = this; + + // function to create a new net layer + this.init = function (self_id, port, done_CB) { + + self_id = self_id || VAST_ID_UNASSIGNED; + port = port || VAST_DEFAULT_PORT; + + // create message handler manager and add self as one of the handlers + var handler = new msg_handler(self_id, port, function (local_addr) { + + // NOTE: this will cause initStates() be called + handler.addHandler(_that); + + // notify done + if (typeof done_CB === 'function') + done_CB(local_addr); + }); + } + + var _initStates = this.initStates = function (msg_handler) { + + if (msg_handler !== undefined) { + + _msg_handler = msg_handler; + + var id = _msg_handler.getID(); + LOG.warn('VAST_client initStates called with msg_handler, id: ' + id); + + // add convenience references + _storeMapping = _msg_handler.storeMapping, + _getID = _msg_handler.getID, + _disconnect = _msg_handler.disconnect, + _sendMessage = _msg_handler.sendMessage, + _sendPack = _msg_handler.sendPack; + + // add matcher as handler + var matcher = new VAST.matcher(); + _msg_handler.addHandler(matcher); + + // create a self node + _self = new VAST.node(_getID()); + + // TODO: move this somewhere? so the above can be extracted as standard + // do constructor work + + // ensure gateway's type is correct + // TODO: validate gateway address + var addr = new VAST.addr(); + addr.parse(_gateway); + _gateway = addr; + + // store gateway address + _storeMapping(VAST_ID_GATEWAY, _gateway); + } + } + + ///////////////////// + // constructor + // + LOG.debug('VAST constructor called'); + + // + // connect with handler + // + + var _msg_handler; + + // convenience references + var _storeMapping, _getID, _disconnect, _sendMessage, _sendPack; + +} + +// export the class with conditional check +if (typeof module !== "undefined") + module.exports = VAST_client; diff --git a/VAST_matcher.js b/VAST_matcher.js index 361323d0..f7a85dbd 100644 --- a/VAST_matcher.js +++ b/VAST_matcher.js @@ -47,7 +47,7 @@ 2012-11-09 initial version (convert from VASTMatcher.h) */ -require('./common.js'); +require('./VAST.js'); var msg_handler = msg_handler || require('./net/msg_handler.js'); // config diff --git a/VON_peer.js b/VON_peer.js index 55793a07..3182b043 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -63,7 +63,7 @@ 2012-07-07 initial version (from VAST.h) */ -require('./common.js'); +require('./VAST.js'); // to be inherited by VON.peer var msg_handler = msg_handler || require('./net/msg_handler.js'); diff --git a/VSO_peer.js b/VSO_peer.js index 23f200e4..2c7a480c 100644 --- a/VSO_peer.js +++ b/VSO_peer.js @@ -73,7 +73,7 @@ 2012/11/01 converted to first js version */ -require('./common.js'); +require('./VAST.js'); require('./VON_peer.js'); diff --git a/SFVoronoi.java b/VSS/SFVoronoi.java similarity index 100% rename from SFVoronoi.java rename to VSS/SFVoronoi.java diff --git a/line2d.java b/VSS/line2d.java similarity index 100% rename from line2d.java rename to VSS/line2d.java diff --git a/common.js b/common.js deleted file mode 100644 index 19e12b8e..00000000 --- a/common.js +++ /dev/null @@ -1,60 +0,0 @@ - -/* - common definitions for VAST.js -*/ - -// -// utilities -// - -var logger = require('./common/logger'); -global.LOG = new logger(); -global.UTIL = require('./common/util'); - -// set default error level -LOG.setLevel(3); - -// -// VAST & VON -// -// ID definitions -global.VAST_ID_UNASSIGNED = 0; -global.VAST_ID_GATEWAY = 1; - -global.VAST = require('./vast_types'); -global.VAST.net = require('./net/vast_net'); -global.VAST.client = require('./VAST'); -global.VAST.matcher = require('./VAST_matcher'); - -// TODO: find a better way to store this? (maybe in msg_handler?) -global.VAST.state = { - ABSENT: 0, - // INIT: 1, // init done - JOINING: 2, // different stages of join - JOINED: 3 -}; - -global.VAST.priority = { - HIGHEST: 0, - HIGH: 1, - NORMAL: 2, - LOW: 3, - LOWEST: 4 -}; - -// TODO: combine into nicer-looking global -global.VAST.msgtype = { - BYE: 0, // disconnect - PUB: 1, // publish request - SUB: 2 // subscribe request -}; - -global.VAST.msgstr = [ - 'BYE', - 'PUB', - 'SUB' -]; - -global.VON = { - peer: require('./VON_peer') -}; diff --git a/net/net_nodejs.js b/net/net_nodejs.js index b7ca1303..bdee0d28 100644 --- a/net/net_nodejs.js +++ b/net/net_nodejs.js @@ -70,7 +70,7 @@ 2012-09-24 add is_connected() */ -require('../common.js'); +require('../VAST.js'); var l_net = require('net'); // allow using network function net_nodejs(onReceive, onConnect, onDisconnect, onError) { diff --git a/server.js b/server.js deleted file mode 100644 index fc28e6b1..00000000 --- a/server.js +++ /dev/null @@ -1,18 +0,0 @@ - -var net = require('net'); - -var server = net.createServer(function (socket) { - - socket.addListener('error', function(e){ - console.log("error occur: " + e); - }); - - socket.write("Echo server\r\n"); - socket.pipe(socket); -}); - - - -server.listen(1037, "127.0.0.1"); -console.log( "server now listens at port 1037" ); - diff --git a/test_VAST_client.js b/test/test_VAST_client.js similarity index 99% rename from test_VAST_client.js rename to test/test_VAST_client.js index 282dff30..9038907d 100644 --- a/test_VAST_client.js +++ b/test/test_VAST_client.js @@ -10,7 +10,7 @@ // flags var AUTOMATIC_LEAVE_PERIOD = 3; // number of seconds -require('./common'); +require('../VAST'); // do not show debug //LOG.setLevel(2); diff --git a/test_VON_peer.html b/test/test_VON_peer.html similarity index 100% rename from test_VON_peer.html rename to test/test_VON_peer.html diff --git a/test_VON_peer.js b/test/test_VON_peer.js similarity index 99% rename from test_VON_peer.js rename to test/test_VON_peer.js index b13af243..468fdf40 100644 --- a/test_VON_peer.js +++ b/test/test_VON_peer.js @@ -8,7 +8,7 @@ // flags var AUTOMATIC_LEAVE_PERIOD = 3; // number of seconds -require('./common'); +require('../VAST'); // do not show debug LOG.setLevel(2); diff --git a/test_VON_scale.js b/test/test_VON_scale.js similarity index 97% rename from test_VON_scale.js rename to test/test_VON_scale.js index 8ccacf82..0f8daa2b 100644 --- a/test_VON_scale.js +++ b/test/test_VON_scale.js @@ -6,8 +6,8 @@ */ // flags -require('./common'); -var cluster_model = require('./move_cluster'); +require('../VAST'); +var cluster_model = require('../move_cluster'); // set error level LOG.setLevel(2); diff --git a/test_fresh.js b/test/test_fresh.js similarity index 100% rename from test_fresh.js rename to test/test_fresh.js diff --git a/test_inhert.js b/test/test_inhert.js similarity index 100% rename from test_inhert.js rename to test/test_inhert.js diff --git a/test_net.js b/test/test_net.js similarity index 100% rename from test_net.js rename to test/test_net.js diff --git a/test_net_nodejs.js b/test/test_net_nodejs.js similarity index 100% rename from test_net_nodejs.js rename to test/test_net_nodejs.js diff --git a/test_voro.html b/test/test_voro.html similarity index 100% rename from test_voro.html rename to test/test_voro.html diff --git a/test_voro.js b/test/test_voro.js similarity index 100% rename from test_voro.js rename to test/test_voro.js diff --git a/test_voro_rh (interactive).html b/test/test_voro_rh (interactive).html similarity index 100% rename from test_voro_rh (interactive).html rename to test/test_voro_rh (interactive).html diff --git a/test_voro_rh (specify points).html b/test/test_voro_rh (specify points).html similarity index 100% rename from test_voro_rh (specify points).html rename to test/test_voro_rh (specify points).html diff --git a/test_voro_rh.html b/test/test_voro_rh.html similarity index 100% rename from test_voro_rh.html rename to test/test_voro_rh.html diff --git a/test_voro_sf.html b/test/test_voro_sf.html similarity index 100% rename from test_voro_sf.html rename to test/test_voro_sf.html From 8e450da2782ad798bc4503a85b6900513bc1528c Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 7 Mar 2013 20:28:01 +0800 Subject: [PATCH 021/141] VSS dependency fix --- NN.bat | 1 - VAST WorkLog.txt | 8002 ----------------------- VAST.js | 7 +- VAST_matcher.js | 6 +- VON_peer.js | 20 +- VSS/VSS.js | 2 +- VSS/handler.js | 4 +- {VSS => misc}/SFVoronoi.java | 0 common_util.js => misc/common_util.js | 0 generic_net.js => misc/generic_net.js | 0 hash.js => misc/hash.js | 0 stable-proxy.js => misc/stable-proxy.js | 0 tools.js => misc/tools.js | Bin net/vast_net.js | 25 +- scale.bat | 1 - test/client.bat | 1 + gw.bat => test/gw.bat | 0 test/scale.bat | 1 + test/test_VAST_client.js | 6 +- test/test_VON_peer.js | 6 +- test/test_VON_scale.js | 4 +- vast.io/vast.io-server.js | 6 +- 22 files changed, 49 insertions(+), 8043 deletions(-) delete mode 100644 NN.bat delete mode 100644 VAST WorkLog.txt rename {VSS => misc}/SFVoronoi.java (100%) rename common_util.js => misc/common_util.js (100%) rename generic_net.js => misc/generic_net.js (100%) rename hash.js => misc/hash.js (100%) rename stable-proxy.js => misc/stable-proxy.js (100%) rename tools.js => misc/tools.js (100%) delete mode 100644 scale.bat create mode 100644 test/client.bat rename gw.bat => test/gw.bat (100%) create mode 100644 test/scale.bat diff --git a/NN.bat b/NN.bat deleted file mode 100644 index 20edbfea..00000000 --- a/NN.bat +++ /dev/null @@ -1 +0,0 @@ -node test_von_peer 37700 127.0.0.1 diff --git a/VAST WorkLog.txt b/VAST WorkLog.txt deleted file mode 100644 index 92e280ec..00000000 --- a/VAST WorkLog.txt +++ /dev/null @@ -1,8002 +0,0 @@ - -To-Do: - -- add VON_REQUEST_ID msg or send VON_HELLO first? (for ID assignment without net layer involvement) -- check if initially only VON_MOVE exists is okay (no VON_MOVE_B for boundary neighbor checks) -- check redundency in messages sent out - - -- check timestamp compare are only for those generated at same host (it's meaningful) -- optimize MOVE event publications (combine event & AOI update into one) -- VoroCast (area publication) - - -Bug List: - -BUG (*) denotes design bug: - ---- -2012-06-23: 1st & 2nd level enclosing neighbors are the same -2012-07-23: voro.overlaps returns 'false' always when there's only one region for any point -2012-08-29: scaling many nodes will cause TCP connection errors -2012-09-24: send_msg in vast_net does not necessarily send out in FIFO order -2012-11-27: shutdown 50 nodes at once will leave ghost nodes -2012-12-01: if node positions overlap, VSS server will show continouing warning messages - ----- -2009-06-12 UDP transmission causes connection lost in real network -2009-07-08 realnet: agent join does not work correctly for over 3 agents or after existing agent departs - - -2009-07-29* if all known AOI peers of a relay's client fail (due to the relay that manages them fail). - after the clients re-join, this relay may be partitioned, as no AOI neighbor would know / be - interested to contact it (so this is a massive failure scenario --> if all your AOI neighbors disappear, - might cause partition) see VAST-case-2009-07-29 - -2009-07-30b some agents would have undeleted objects in multiple arbitrators (probably not receiving OBJECT_D properly) -2009-08-07 after ownership reclaim is enabled, some nodes inside the 1st arbitrator's region would disappear -2009-08-21 avatar objects remain visible after agents have moved out of AOI (caused by having deletion buffer) -2009-09-29 linux server would attempt to re-connect to lost agents and freeze -2010-04-30 too many relays are removed - -- ghost objects at client-view -- each client subscribes more than once, (though may be harmless, but re-subscription should be avoided) -- client crash (in stress test, some clients seem to depart early, about 5%) -- gateway crash - join rate of 1 node / sec and 500 nodes, gateway crashes after roughly 200 nodes - in 300 nodes, 2 sec / join, gateway would seg fault -- join retry timeout is buggy -- 2010-10-06 after repeated join/leave of worlds, cannot join again (gateway could not assign origin matcher) - -Overall Goals: -implement generic SPS based on matcher-client design -simplify msg_handler usage (handler needs to implement many things...) -debug ECONNABORTED (possibly over-connection), EFAULT - -2013-03-05 (2) -------------- -goal: identify ENOBUFS causes - -- found that there's a lot of redundent socket connections - - - -2013-03-04 (1) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - debug ENOBUFS - -- ENOBUFS is caused by lack of memory in server buffer - - -2013-02-26 (2) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - debug ENOBUFS - -- appearantly there are many 'incoming' sockets whose real ids were not assigned (but their socket - connections are kept) - > may need to check if incoming sockets handshake their ids properly - - -2013-02-25 (1) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - -- tested slowly joining nodes and let them run, seems more stable -- basic VON seems to work for up to 50 nodes, but then many nodes do not move - (appears to be disconnected) - -2013-02-23 (6) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - -- still can't pinpoint the problem... - -2013-02-19 (2) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - -- found out if nodes join slowly, then less likely to crash (can last longer) - - - -2013-02-05 (2) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - - - -2013-02-02 (6) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - -- found out the 'data' event and 'connect' event do not trigger in particular orders. - so might need to be careful to assume 'connect' always come before 'data' - - -2013-02-01 (5) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - -- after showing socket connection size and print out actually known sockets - found out too many sockets have negative socket numbers - (so likely there are over-connections every time a certain socket is the send target) - -2013-01-29 (2) -------------- -goal: debug ECONNABORTED (possibly over-connection), EFAULT - -BUGS known: --0:36- [11] msgqueue has no target [2]. shouldn not happen -0:36- [16] got VON_HELLO_R from unknown neighbor [12] - - - -2013-01-27 (7) -------------- -goal: debug ECONNABORTED (possibly over-connection) - -- try to perform socket.end() whenever duplicate socket is detected -- new error: EFAULT - -net.js:912 - if (e.errno != EMFILE) throw e; ^ -Error: EFAULT, Bad address - at IOWatcher.callback (net.js:910:24) - - - -2013-01-26 (6) -------------- -goal: debug vast.io client - -- seems vast.io local client works now -- will have ECONNABORTED cause von_test_scale to crash with 50 nodes on Toshiba NB -- see lots of 'redundent socket already exists' warning - - -2013-01-22 (2) -------------- -goal: debug vast.io client - -- re-check & ensure that disconnect() on socket works - - -2013-01-21 (1) -------------- -goal: debug client id assignment - -- client id assign ok. -- need to debug vast.io client - -2013-01-20 (7) -------------- -goal: debug id assignment - support send to self id in vast_net's send() - -- debug gateway id assignment (now need to debug client ID assignment) -- also provide send to self in vast_net's send() - -2013-01-15 (2) -------------- -goal: debug id assignment - - -2013-01-12 (6) -------------- -goal: debug id assignment - remove id assign from net layer - -- removed id assign - -2013-01-11 (5) -------------- -goal: debug id assignment - -- need to remove id assignment from net layer - -2013-01-10 (4) -------------- -goal: debug id assignment - - -2013-01-08 (2) -------------- -goal: review _processData in vast_net for id assignment mechanism - -- implement id assigbment but is buggy -- need to debug id assignment - - -2013-01-05 (6) -------------- -goal: store sender id to all out-going messages at vast_net - -- modified send() in vast_net to accomodate sending multiple targets at once, - while attaching sender id - -- need to review _processData in vast_net for id assignment mechanism - - -2013-01-03 (4) -------------- -goal: store sender id to all out-going messages at vast_net - -- finished review net_nodejs -- found out need to decide where to pack/unpack sender id - - - -2012-12-30 (6) -------------- -goal: combine incoming & outgoing sockets into same management - test code - -- review net_nodejs & vast_net for correctness - -2012-12-22 (6) -------------- -goal: combine incoming & outgoing sockets into same management - check vast_net / net_nodejs modifications - test code - -- modify vast_net behavior to manage only sockets. - -2012-12-21 (5) -------------- -goal: combine incoming & outgoing sockets into same management - test code - -- figure out should manage all sockets in vast_net, but not net_nodejs objects. - - -2012-12-17 (1) -------------- -goal: combine incoming & outgoing sockets into same management - -- did code review/proofreading - -2012-12-16 (7) -------------- -goal: combine incoming & outgoing sockets into same management - -- finish remove _sockets in vast_net (pending tests) - - -2012-12-15 (6) -------------- -goal: modify msg_id so all messages' sender id is contained with message itself - -main issues now: -- need to allow remote node learn of id --> notify / contain id -- handshake & regular messages can be not sequential --> consider self-contain id -- redundent connections for same host:port pairs --> store out & in-coming sockets into same reference -- messages can pack up (queued up & send all at once) --> consider UDP - - - - -2012-12-14 (5) -------------- -goal: debug received NaN for sender at net layer - -- ensure that the message received is turned into js object first, before the 'sender id' (src) is retrieved. - now the NaN problem is solved. :) - -- now can run 50 nodes for a long time (10min+) However, two issues are observed: - 1. once 50 nodes are joined, it's difficult (almost impossible) to join a new node via web - 2. node movement in demo is very slow & not smooth - 3. nodes get discovered all at once - 4. many messages are accumulated. (change to UDP?) - - -2012-12-13 (4) -------------- -goal: debug received NaN for sender at net layer - experiment with include 'sender id' in all messages sent - -- try adding 'sender id', but still seems to get NaN id problems. Really need to look into the mechanism - for connections. - -2012-12-12 (3) -------------- -goal: debug received NaN for sender at net layer - -- found that the problem with dual-connection is not easy to solve. A case might go like this: - - 1. A sends hello to B (making new connection...) - 2. B sends hello to A (making new connection...) - 3. A rejects B's 'notify id' (there's already a connection established in 1.) - 4. B's message to A cannot be processed (source unknown 'NaN' problem) for the following: - 5: HELLO - 7: EN - 10: MOVE_B - 10: MOVE_B - 10: MOVE_B - 6: HELLO_R - 4: NODE - - why does 3 occur? (in theory, id notify goes only via a newly established connection...) - need to think more carefully about the dual-connection issue... - -- consider to enclose sender id in packet (makes it completely self-contained), so that it doesn't - matter which channel is used to deliver the message. This will be more RESTful? (and possibly can - become a HTTP-style protocol in future)? - - -2012-12-11 (2) -------------- -goal: debug received NaN for sender at net layer - -- found out NaN problem may be caused by id notify is sent to an already-established - socket connection. - - -2012-12-08 (6) -------------- -goal: debug received NaN for sender at net layer - -- try to set socket.connected flag *after* connect notify callback is called, - so that id notify may not be sent after other messages. - > does not solve the NaN id problem. - - -2012-12-06 (4) -------------- -goal: debug received NaN for sender at net layer - -- found out the bug is caused by ID notify and subsequent messages are mixed in sending order. - so later messages are received before the ID notify is processed. need to find a way to ensure - ID notify is sent absolutely first before all other messages. - > put ID notify to beginning of msgqueue to be sent to remote node, but NaN problem still exists... - > an explanation is the socket.write() does not actually preserve the order of writing, - > if message is put to system buffer due to too busy.. - -- found another problem is caused when two nodes initiate connections to each other simutaneously... - so possibly two channels are established... - > need to find a way to ensure just one connection - -- yet another problem is messages are sent from existing to new node in this order: - HELLO, EN, NODE, id notify.. - - possiby acceptor first discovers new node, then got VON_JOIN? or acceptor - another possibility is that the messages come from different connections - (one from self, another from remote). - - -2012-12-05 (3) -------------- -goal: debug received NaN for sender at net layer - -- notice that when nodes are joining quickly, VON_HELLO can be received earlier than VON_NODE for a joining node - (that means node is not yet fully joined, but other neighbors are already saying HELLO)... - -- change sending initial ID to wait till socket.write is complete. -- also change all messages can be processed once initial ID is obtained - -- bug still exists, looks like we need a more guaranteed way to notify unique ID - - -2012-12-03 (1) -------------- -goal: remove tick() from VON_peer.js - -- done removing tick() and spread original tick functions to after receiving VON_NODE - (contactNewNeighbor) and VON_MOVE_X (checkNeighborDiscovery & removeNonOverlapped) - - - - -2012-12-01 (6) -------------- -goal: implement subscribe() in VAST.js (matcher-side) - -BUG: if node positions overlap, the two nodes will continously have check enclosing neigbhor - error (as actual neighbor size is smaller than expected # of neighbors) - - - - -2012-11-29 (4) -------------- -goal: implement subscribe() in VAST.js (matcher-side) - extract non-standard code from init() in VON_Peer - -- revise init & join responisbility, and make init simply to initialize, - any network contact is done only at the join() stage - -- finish revision & testing (but on small NB, running 50 nodes will still cause - program fault/hang after a while, likely due to network problem). - - -2012-11-28 (3) -------------- -goal: implement subscribe() in VAST.js (matcher-side) - -- identfiy a potential issue: as init in VON_Peer.js performs necessary tasks other - than msg_handler creation, it's required to include it, but then msg_handler will - be created twice when VAST_matcher tries to include a VON_Peer inside itself. - solution seems to keep init() simple so it's the same for VON_Peer, VON_matcher, or VAST - -2012-11-27 (2) -------------- -goal: fix port does not close after revokeNode is called in VSS - -- added shut() to when VSS server performs revokeNode (seems to solve the issue) - -BUG: when running test_VON_scale with 50 nodes, and shutdown all 50 nodes, - some nodes do not seem to disappear cleanly and still exist in the knoweldge of still-existing nodes - (need to somehow find a way to clean them, or to periodically ping / verify their existence) - - - -2012-11-26 (1) -------------- -goal: implement subscribe() in VAST.js (matcher-side) - -- stuck at how to initialize vast_matcher (need to figure out: how to initialize a matcher) -- added 'close()' function in net_nodejs & vast_net to close up a listening server - - -2012-11-25 (7) -------------- -goal: implement subscribe() in VAST.js (matcher-side) - -- re-check basic client-side implementation -- decide that 'host_id' in subscription info is still important (to forward matching publications) - - -2012-11-24 (6) -------------- -goal: implement subscribe() in VAST.js (matcher-side) - -- consider to remove host_id in 'endpt' and 'sub' data structures - - -2012-11-23 (5) -------------- -goal: implement subscribe() in VAST.js (matcher-side) - -- need to consider two issues: - whether host_id is necessary in a subscription - whether each vast node can do multiple subscriptions (or limited to single) - - -2012-11-22 (4) -------------- -goal: make VON_peer to work with VON_PING to obtain initial ID - -- add support of VON_PING and modify join procedure -- modify relevant programs that use VON_peer for new interface (vast.io-server, test_VON_peer, test_VON_scale) - -- modify behavior: if a node unsubscribe (with vast.io) it will not be listed as subscriber even when node positions - overlap (previously it simply set subscription radius to 0). - -KNOWN ISSUES: -- seems working for up to 50 nodes via test_VON_scale. but there are many - "got VON_HELLO_R from unknown neighbor [xx]" warnings - also many remote id is NaN & negative ID are received... many are VON_HELLO, VON_EN, VON_MOVE & VON_MOVE_B - -- continous debug msg being printed when nodes have overlapped position - - -2012-11-21 (3) --------------- -goal: implement subscribe() in VAST.js (matcher-side) - -- modify vast.io-server & test_VON_scale to suit new VON_Peer usage - -BUG: only a few nodes can join successfully (out of 50) - > cause is that when VON_JOIN is received, it considers the packet coming from gateway, so - the acceptor replies to gateway only (and not the joining node) - solution will be to separate ID obtain from query/join (add a VON_PING message) - - -2012-11-20 (2) --------------- -goal: debug query() in VON_Peer - -MILESTONE -- done debug, now can join up to 3 nodes using VON_QUERY to send join request - -2012-11-19 (1) --------------- -goal: a flexible & efficient query() in VON_Peer - -- implemented query() in VON_Peer - -2012-11-18 (7) --------------- -goal: a flexible & efficient query() in VON_Peer - -- figure out we need a 'forward' action to a certain destination - can use 'forward' to send 'VON_JOIN' and 'VAST_SUB' - - -2012-11-17 (6) --------------- -goal: extract query() from join() in VON_Peer - -- split original join() into query() & join() in VON_Peer, - but discover that handling of VON_QUERY message actually currently returns initial - neighbor list, and therefore cannot be split to query response & join response easily. - without adding join latency (first got query, then send out another join request). - -2012-11-16 (5) --------------- -goal: implement subscribe() in VAST.js (matcher-side) - -- found out may need to extract VON_QUERY in VON_peer into more general form, - so both VON and upper layers (such as VAST/VSO) can use it directly... - might need to change VON join procedure into: - - query for acceptor of the joining locatin - - join request to acceptor directly (either accept or refuse) -- possibly can use this for Relay search as well... - - -2012-11-15 (4) --------------- -goal: implement subscribe() in VAST.js - -- done, but need to test - -2012-11-14 (3) --------------- -goal: implement subscribe() in VAST.js - -- add sub datatype to vast_types for 'subscription' record - - -2012-11-13 (2) --------------- -goal: implement join() in VAST.js - -- keep converting C++ join procedure to js - -DESIGN CHANGES (simplify things) -1. remove 'world_id' parameter when joining VAST - (keep VAST itself simpler, we can use other meta-mechanisms for multiple world support) -2. add 'query' method to VON_Peer, so a joining VAST client can find its initial owner matcher -3. remove 'join' 'leave' procedures in VAST (client), as querying can be done when subscribing -4. remove 'group' parameter from VAST.pack (packet format to transmit) - - -2012-11-12 (1) --------------- -goal: implement join() in VAST.js - -- port join C++ code to js -- send world_id successfully.. - -2012-11-11 (7) --------------- -goal: implement join() in VAST.js - -- test successfully sending an initial message from VAST client to gateway, and all handlers can process the message - - -2012-11-10 (6) --------------- -goal: implement join() in VAST.js - -- make NodeState global as 'VAST.state' - make VON_Priority global as 'VAST.priority' - -- allows VON peers to store key/value pairs which can be migrated automatically? - (this will allow for load balancing, also a simpler design for VON peers that - supports VSO, as well as other things, e.g., VSS servers' 'ident' info exchange) - -- review main functions for VAST.js (VAST.js architecture file) - -2012-11-09 (5) --------------- -goal: implement join() in VAST.js - -- figure out will need to have both VAST and VAST_matcher logic to implement join() - - -2012-11-08 (4) --------------- -goal: make VAST.js and VON_Peer.js work with same network layer - revise how msg_handler processes net layer - -- finish revise msg_handler mechanism, can run correctly - -MILESTONE -- VAST.js & VON_Peer.js now use same network layer - -2012-11-07 (3) --------------- -goal: make VAST.js and VON_Peer.js work with same network layer - revise how msg_handler processes net layer - -- put net object inside msg_handler and hide it - - -2012-11-06 (2) --------------- -goal: make VAST.js and VON_Peer.js work with same network layer - -- try to use VON_peer within VAST.. found out need to revise how network binding is done - - -2012-11-05 (1) --------------- -goal: make multiple handlers work with same network layer - -- finish implement a version, but calling sendMessage/sendPack has incorrect context... -- change from prototype usage to all internal / private methods (seems to work correctly) - - -2012-11-04 (7) --------------- -goal: make multiple handlers work with same network layer - -- convert msg_handler to support addHandler/removeHandler functions -- pending converting VON_peer to use new msg_handler class - - -2012-11-03 (6) --------------- -goal: integrate VAST.js to use msghandler with VONPeer - implement join() in VAST.js - -- try re-designing VONpeer as a pluggable handle to msg_handler - - -2012-11-02 (5) --------------- -goal: integrate VAST.js to use msghandler with VONPeer - implement join() in VAST.js - -- checking what's required for a join procedure in VAST.js - -2012-11-01 (4) --------------- -goal: make multiple handlers work with same network layer - add a VAST_Client handler - -- merge VON_BYE with VON_DISCONNECT into VON_BYE - -- review VAST.js interface definition (adopted from VAST_Client.h) - -2012-10-31 (3) --------------- -goal: make multiple handlers work with same network layer -- study how to add another logic layer on top of current message handler - -2012-10-30 (2) --------------- -goal: VSS server to support ident exchange - -MILESTONE: -- VSS server can now exchange meta data for the VON nodes created independently ^^ - -- known issue: a remote node's ident may not be discovered immediately after a node performs publishPos. - will wait till the next publishPos is performed. - - -2012-10-29 (1) --------------- -goal: VSS server to support ident exchange - -- fix 'meta' field exchange in VON peer (now storing & sending via HELLO & HELLO_R works correctly) - - -2012-10-28 (7) --------------- -goal: add put() & get() functions to VON Peer - -- implemented & tested storing & sending meta-data via VON_HELLO/VON_HELLO_R. -- still to do: storing & retriving node ident as meta-data at VSS servers - - -2012-10-27 (6) --------------- -goal: test whether VSS server can scale on different machines - -- setup VSS servers on both dev & prod servers, found that node discovery works correctly ^^ - however, node ident is not shared across different VSS servers, so - subscriber list did not return correctly (need to find a solution for this...) - -- try to transfer node-specific info along with HELLO / HELLO_R messages - add put() and get() to VON_peer's interface... - - -2012-10-26 (5) --------------- -goal: make subscriber list available upon first registerNode - -CHANGE: modify join behavior in VON: neighbor list is build immediately after VON_NODE is received. - not after VON_HELLO_R is received. This makes it quicker to discover initial list of neighbors - (save one less round-trip). - -- subscribe list available upon join finished & tested. - - -2012-10-25 (4) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - make multiple handlers work with same network layer - modify VSS response - -- fixed response & deploy -- found a small issue: after initial publishPos, (registerNode), subscriber list is not - obtained (only valid after 2nd publishPos call). - - - -2012-10-24 (3) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - make multiple handlers work with same network layer - modify VSS response - debug msg_handler mechanism (only one 'net' instance) - -DEBUG: 2012-10-23 - > bug solved by making sure we don't use any variable within the parent class, - all variables in a prototype-inherted parent have only one instance and are shared - so we need to make sure instance-specific variables can all be independent - this is resolved by passing instance-specific functions via the parent class's constructor - - -2012-10-23 (2) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - make multiple handlers work with same network layer - modify VSS response - debug msg_handler mechanism (only one 'net' instance) - -- found out one problem is that by using 'prototype', only one copy of the variable - exists, thus causing problems if multiple instances may access the variable. - solution is to make the variable 'instance-based' (making it 'this.xxx') - but then it may not be accessible from callback functions (scope problem) - -- after applying right context (using 'apply') seems to work with prototype - -BUG 2nd node via VSS cannot properly join & obtain VON_NODE response from gateway - appearantly gateway forward VON_QUERY to not an existing node, but the new node - - - -2012-10-22 (1) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - make multiple handlers work with same network layer - modify VSS response - debug msg_handler mechanism (_net not found) - -- modify all public methods & variable in msg_handler to adopt 'prototype'-style definition - can run, but 'net' reference is still shared by ALL created msg_handler instances - (desired behavior is that each msg_handler instance has its own 'network' layer) - -2012-10-21 (7) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - make multiple handlers work with same network layer - modify VSS response - -BUG: _net reference is not found (when VON_peer called a 2nd time) - while creating VON_peer via the VSS server - - - -2012-10-20 (6) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - modify VSS response - debug using msg_handler class (js inhertance work) - -- modify to pass packetHandler as part of the init() parameter to parent class - > solve the issue of parent class cannot call override function in child class - (issue found on 2012-10-17). - - - -2012-10-17 (3) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - modify VSS response - debug using msg_handler class (js inhertance work) - -- tested out using msg_handler successfully, in progress of converting -- found out an issue: parent class cannot call / use methods override by child class - -2012-10-14 (7) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - modify VSS response - debug using msg_handler class (js inhertance work) - -- msg_handler inhertance works under test_vast_net - - -2012-10-13 (6) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - debug using msg_handler class (js inhertance work) - identify why inhertance doesn't work - -- made sample code inhertance work with prototype - -2012-10-12 (5) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - debug using msg_handler class (js inhertance work) - -- verified js inhertance indeed work in small test program - - -2012-10-11 (4) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - debug using msg_handler class (js inhertance work) - -- will try base = prototype approach (suggested by Shine) - did not seem to work... - -2012-10-09 (2) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - debug using msg_handler class (js inhertance work) - -- study js inhertance - -2012-10-08 (1) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - debug using msg_handler class (js inhertance work) - -- study js inhertance - -2012-10-07 (7) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - debug using msg_handler class - -- study javascript inhertance approaches - -2012-10-06 (6) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - debug /net directory layout & using msg_handler class - -- done test_vast_net debug - - -2012-10-05 (5) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - -- start moving network code to /net directory (will crash) - - -2012-10-04 (4) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg_handler - -- init order in VAST C++ - - create net - - create & join relay - - create & join matcher - - init done - - ordering, dependency, consistency - - -2012-10-03 (3) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - -- start making msg_handler.js class - -2012-10-02 (2) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - -- still stuck & consider how to do module separation - -2012-10-01 (1) --------------- -goal: extract non-VON-specific code from VON_peer as common code for msg handlers - identify logical separation for matcher/relay/client/VON - -- recheck 9-14 & 9-11 notes -- modify VSS server response for publishPos as subscriber list - -BUG 50 node simu still crashes wtih error: EMFILE, EFAULT - when nodes are suddenly terminated (under windows) - -2012-09-30 (7) --------------- -goal: disconnection handling when nodes abnormally depart - -- fix connect/disconnect handling in net_nodejs.js - -DEBUG when a 50 node simulation process shutdowns, the gateway crashes with ECONNREFUSED error - > fixed by catching 'error' event when making out-going connections - -DEBUG after 50 node simu ends, the gateway can no longer accepts new clients, - as gateway still thinks those already disconnected peers exist.. - appearantly disconnection is not handled correctly - > fixed by re-ordering when disconnect_handler is called - (should be called in 'close' not 'end') - -MILESTONE: - can now run 50 nodes, and shutdown 50 nodes (then re-join) without problems. - - -2012-09-29 (6) --------------- -goal: ident discovery among neighbors - limit get subscriber result within AOI - -- ident discovery & AOI limiting done - -BUG: when VSS server or vast.io server shutdown, it seems like the VON topology is not - updated accordingly (correctly), and ghost nodes are still left on the system - (disconnection not handled correctly?) - -BUG: when VSS server shutdowns sometimes other still existing nodes (gateway) - will get EPERM error and crash - - - -2012-09-28 (5) --------------- -goal: debug & test JSON response - debug node register / revoke - -MILESTONE -- fix response format to JSON and deploy to dev server -- functions still missing: - need to support neighbor node's ident discovery - need to respond neighbor list whenever doing publishPos - - -2012-09-27 (4) --------------- -goal: debug & test JSON response - debug node register / revoke - -- review current code & procedure. - - -2012-09-26 (3) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - connect VSS functions with VON peer - debug VON peer usage from VSS server - allow ident be queried when get subscribers - add revokeNode to VSS function - -- revokeNode & registerNode added - -2012-09-25 (2) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - connect VSS functions with VON peer - debug VON peer usage from VSS server - debug send_msg mis-ordering - -for the 2012-09-24 bug: -- add queuing up messages to be sent, if connection is not yet established. - can run till 50 nodes ok (on PC), but crash due to EADDINUSE for 100 nodes - but we still get remote id is NaN error - -- might consider add explicit id exchange during handshake, to ensure remote id is always available. - -- seems the NaN id is reduced (for 50 nodes), but some nodes still get NaN id - at times (esp. as the # of nodes increases). - - - -2012-09-24 (1) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - connect VSS functions with VON peer - debug VON peer usage from VSS server - -- test again test_VON_scale for scaling VON server @ dev server. can run up to 50+100 = 150 nodes. - however, client movement in browser stops when all nodes are joined - - when all existing nodes at server are closed: - gateway (test_VON_peer) experience this: connect ECONNREFUSED and it'll crash - -- test run 100 VON nodes @ dev server, seems to continously moving notice the following: - * CPU is used at 100%, RAM 10 - 15% (out of 512MB, left 168 MB) - * CPU is used by the test_VON_scale - * movement becomes slow after a while (data accumulated?) - * file handles were in the range of 10,000.. while after termination, only 700 file handles are used normally - -BUG: found a bug caused by the asyncrous nature of js function calls - when a newly joined VON peer tries to send HELLO / EN to a newly learned neighbor, - the EN message can be sent *BEFORE* the HELLO is sent (or before the connection is established) - the reason is that when sending the first HELLO message, connection is yet established, - so an attempt is made to connect. - but this is returned directly, so the next message is sent and stored. - when the connection is finally made, the 2nd message gets delivered first before the first message. - - basically we cannot assume that two messages will be sent in a FIFO order. - - -2012-09-23 (7) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - connect VSS functions with VON peer - test VON peer usage from VSS server - -- done integrate - publishPos - subscribeNearby - unsubscribeNearby - querySubscribers - - -2012-09-22 (6) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - connect VSS functions with VON peer - -- done parameter extraction from RESTful request -- publishPos, subscribeNearby done preliminary integration with VON peer - -2012-09-21 (5) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - connect VSS functions with VON peer - -- implement basic functions wtih VSS server, will now need to use & connect them with VON peer - - -2012-09-20 (4) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - parameter breakout & analysis - -- done parameter breakout - - -2012-09-19 (3) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - build VSS server (to take & respond RESTful pub/sub commands) - -- done creating basic layout for VSS server - -2012-09-18 (2) --------------- -goal: extract non-VON-specific code from VON_peer as common code for handlers - -- add VSS server binding to provide pub/sub functions -- study about js inhertance & prototype mechanism - -2012-09-17 (1) --------------- -goal: VAST porting - basic pub/sub functions - implement VASTClient - implement join() - build sharable network layer (for VASTClient & VON) - extract non-VON-specific code from VON_peer as common code for handlers - -- still stuck at not sure how to extract net layer so the following effects are achieved: - * multiple logics supportable on same net layer - * logic are independent of each other (can be modularly inserted/removed) - * do not use a singleton (so that multiple net instance can exist in same process) - - -2012-09-16 (7) --------------- -goal: VAST porting - basic pub/sub functions - implement VASTClient - implement join() - build sharable network layer (for VASTClient & VON) - -- evaluate and decide to follow original VAST C++ approach (message handler) - for message processing, pending fixes for js-specific behaviors. - - -2012-09-15 (6) --------------- -goal: VAST porting - basic pub/sub functions - implement VASTClient - implement join() - build sharable network layer (for VASTClient & VON) - -- create message_handler.js - can plugin independent logic to handler, while sharing a common network layer for send/recv - - required methods: - - processPack() handle incoming messages relevant to this handler - sendMessage() send a message to one or more targets at a different handler - tick() perform incoming message processing - - -2012-09-14 (5) --------------- -goal: VAST porting - basic pub/sub functions - implement VASTClient - implement join() - build sharable network layer (for VASTClient & VON) - -- need to consider: - whether functions of VON/VSO/SPS (& possibly future VoroCast) can be completely de-couple? - also the net layer can be physical or simulated - in other words, any one of them can be enabled/disabled. - -a possible division of labor: - -VON: peer connectivity, neighbor discovery within peer's AOI - - find gateway and join network - - position movement - - update from neighbors - -SPS: pub/sub record keeping, pub/sub matching, request passing - - pub/sub request - - sub record storage - - pub request passing to neighbors - - pub/sub matching - - delivery of messages - -VSO: region resizing, pub/sub record migration, fault-tolerance (?) - - load detection - - overload handling (resize) - - overload handling (join) - - underload handling (resize) - - underload handling (leave) - - load migration (ensure consistency & fault-tolerance) - -VCAST: request forwarding - - -Dependencies: -VSO -> VON -VCAST -> VON/VSO -SPS -> VON/VSO/VCAST - - -2012-09-13 (4) --------------- -goal: VAST porting - basic pub/sub functions - implement VASTClient - implement join() - build sharable network layer (for VASTClient & VON) - -- reorganize join procedure. -- decide to use singleton mode for net layer sharing - -2012-09-12 (3) --------------- -goal: VAST porting - basic pub/sub functions - implement VASTClient - implement join() - - -- see that the network layer needs to be shared by VON, VAST, VSO... -- might need to pass in net layer to VON (and also VASTClient), - and create a separate MessageHandler class (any other way?) - - - -2012-09-11 (2) --------------- -goal: VAST porting - basic pub/sub functions - layout modular framework (for independent features) - - -- upload vast.js codebase to github - -- basic idea for making VASTClient, VASTRelay, VASTMatcher modular: - -VASTMatcher: - support for pub/sub matching should exist at VASTMatcher - it deals with pub/sub record keeping & matching - VASTMatcher can be implemented with either VON or VSO, - the latter will provide dynamic load balancing among matchers. - if only the former is used, then only static partitioning is available. - - basic matcher functions: - * accept subscribe request (from VASTClient) - * accept publish request (from VASTClient) - * forward publish request to neighbors - * deliver publications to relevant subscribers within own region - * list currently known subscribers - * join a VON network and maintain topology with neighbors - - -VASTRelay: - support physical coordniate identification and connection to closest Relay - -VASTClient: - basic client functions to forward all requests to current "owner matcher" - and get all responses from "owner matcher" (possibly via Relay) - - - - -2012-09-10 (1) --------------- -goal: VAST porting - basic pub/sub functions - - -- review & learn that a VAST node actually has three components: - VASTClient - end user pub/sub requests - VASTMatcher - server that handles pub/sub matching - VASTRelay - locate and connect with physical neighbors - - need to think about how to make feature-adding modular - - -2012-09-09 (7) --------------- -goal: VSO porting - basic pub/sub functions - -- create VSO_peer.js file and build initial API methods - - -2012-09-08 (6) --------------- -goal: VSO porting - basic pub/sub functions - debug asymmetric AOI - -DEBUG: -2012-09-07: aymmetric AOI doesn't work - the asymmetric AOI bug disappears if 'radius' is not updated from webpage input - it means that updating AOI radius thus might have corrupted the data - suspect it's caused by parameter passing in that do not conform to VAST types - > check if all input to VON_peer, will conform to internal data structure format - > seems to solve the problem - - -2012-09-07 (5) --------------- -goal: VSO porting - basic pub/sub functions - -- allow position (keyboard input) & AOI-radius update to be reflected on GUI - -BUG: asymmetric AOI does not seem to work - - -2012-09-06 (4) --------------- -goal: run 100+ nodes at Linux server (dev server) - - -BUG -new error when running 100 VONpeers on gateway: EMFILE - -- seems workable for some time, though still will get EMFILE error -after some time (20min+) the program will still be terminated by system - - -2012-09-05 (3) --------------- -goal: verify can run for long time.. (if all exceptions are caught) - -ECONNRESET error (when other peer disconnects) - -- seems like we can run for sometime before running into the 10055 error - - { [Error: connect Unknown system errno 10055] - code: 'Unknown system errno 10055', - errno: 'Unknown system errno 10055', - syscall: 'connect' } - -eventually there's the error: - - FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory - -possible cause is that as TCP does not free connected sockets immediately (do so after 240 seconds), -sockets are occupied and not released, causing available sockets to be used up, if frequent connect/disconnect is used - -see: http://www.proxyplus.cz/faq/articles/EN/art10002.htm - - - -2012-09-04 (2) --------------- -goal: debug scaling connection problems - debug EADDRINUSE - pinpoint EADDRINUSE place -ERCONNR - -- still cannot locate where error occurs -- possibly will change course first and develop support for pub/sub first - - - -2012-09-03 (1) --------------- -goal: debug scaling connection problems - debug EADDRINUSE - pinpoint EADDRINUSE place - -- try to catch exception in 'net_nodejs.js' by warpping 'exception' & 'catch'. - however, is there a cleaner approach? - -BUG after running 50 nodes for a while.. (using test_VON_scale.js mostly) - a node in test_VON_scale would crash due to EADDRINUSE. - -BUG "no mapping exist for send target" (how did it happen? is it normal?) - - - - - -2012-09-02 (7) --------------- -goal: debug scaling connection problems - debug EADDRINUSE - -- found out binding to default 37700 port could fail (no response) after already binded - - - -2012-09-01 (6) --------------- -goal: debug scaling connection problems - debug EADDRINUSE - -process.ic -- learn that EADDRINUSE is caused by binding to same port. might be caused by - running out of ports - -- 50 nodes on NB is fine, but not 100 nodes - -2012-08-31 (5) --------------- -goal: debug scaling connection problems - pinpoint cause of individual errors (localize it to code sections then catch it) - -- appears nodes would overlap on same positions (possibly the cause for the voro site < actual nodes problem) - - - -2012-08-30 (4) --------------- -goal: debug scaling connection problems - - -BUG run 200 nodes at localhost of windows client (crash due to error 10055). - many errors if running for prolonged time - - while running: - EADDRINUSE - no mapping exist for send target XX - - when lots of nodes disconnect: - ECONNRESET - ECONNREFUSED - - -- ports currently binded (used) for a few selected nodes: 6, 9, 24, 20 (running 50 nodes) - so neighbor count seems to be in the 'alright' range - - -2012-08-29 (3) --------------- -goal: massive node movement demo - -MILSTONE -- catch all exception errors, now can relatively stably run to 200 nodes at dev server - -BUG: appears there are problems with frequent connect/disconnect - after a while many errors still to appear, some could crash the program - - - -2012-08-28 (2) --------------- -goal: massive node movement demo - - -- port clusterMovement from C++ code in VAST to javascript - - - -2012-08-27 (1) --------------- -goal: build connector to server-side vast node via websocket - basic GUI control in browser - debug 2nd node see only one neighbor - - -DEBUG 2012-08-17: vast.io server hangs after several clients join (clients > 3) - - > the enclosing neighbor not found bug is caused by having boundries set incorrectly (the top boundary should be - smaller than 'bottom' boundary) - -DEBUG when nodes leave, still existing nodes do not see their departure - > two causes: - 1. removeNonOverlapped is not called (now try to call it periodically during tick) - 2. sendBye does not work properly (when a client disconnects from vast.io server, it should - generate a disconnect event, which should in turn cause leave() be called, - and sendBye be called - -MILESTONE - seems like node join / leave / neighbor discovery all works properly! ^^ - basic GUI also works (movement & display) - - -2012-08-26 (7) --------------- -goal: build connector to server-side vast node via websocket - basic GUI control in browser - debug 2nd node see only one neighbor - -BUG: seems like no halfedge is produced after 2nd node joins - (via vast.io mechanism), so node [1] is not enclosing neighbor - to node [2] - > test with test_VON_peer, appears that there's also 0 halfedge after - 2nd node joins (yet it still returns node [1] as enclosing neighbor) - - -2012-08-25 (6) --------------- -goal: build connector to server-side vast node via websocket - basic GUI control in browser - -- vast.io client&server function implemented - - - -2012-08-24 (5) --------------- -goal: build connector to server-side vast node via websocket - basic GUI control in browser - debug VON_QUERY forwarding / neighbor discovery - - -DEBUG initial neighbor check by GW does not notify for enclosing neighbors to a joiner -2012-08-23: some nodes only connect with gateway - - > seems like the problem is caused by GW node, not able to maintain enclosing neighbor relations properly - so when it tries to notify additional new neighbor first to a joiner, it only finds itself as being relevant - - > after some probing, seems like the EN mechanism at GW is somewhat flawed. - where only GW itself is identified as the EN for a newly joined node. appearantly EN list is not built properly - - > it's found because the boundary range in vast_voro is set to [10000, 10000] but the acutal nodes - move to negative coordinates, the Voronoi computation thus is only partial. The range is now enlarged to - [-10000, 10000]. seems to work better - - -2012-08-23 (4) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - basic GUI control in browser - debug multiple logins via HTML cannot get IDs - -DEBUG 2012-08-22 - found out when using MOVE to update position to other neighbors, as 'addr' field is empty, - but it's also updated to other nodes, so original addr is erased. - solved by updating how MOVE data is being processed, so only provided fields will replace old (not empty ones) - -BUGS: - 1. some nodes only learn of gateway - 2. all nodes always connect to gateway - 3. no neighbor discovery is done beyond original contact from server - - -2012-08-22 (3) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - basic GUI control in browser - debug multiple logins via HTML cannot get IDs - -- seems like joining works, but nodes keep add up.. num of neighbors is not bounded - -BUG: some address returned by gateway is invalid, so contacting them would fail for a joining node. - - - - -2012-08-21 (2) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - basic GUI control in browser - debug multiple logins via HTML cannot get IDs - -- modify join() procedure in VON_peer a bit, so that listen to - socket and detecting self IP are both done, before returning - a join success - - -2012-08-20 (1) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - basic GUI control in browser - debug multiple logins via HTML cannot get IDs - -BUG: -- appears stuck at when 4th node joins, some messages are passed from gateway to other exiting nodes (but fail). However, for some reasons these traffics are blocked so can't tell. - - -2012-08-19 (7) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - basic GUI control in browser - debug multiple logins via HTML cannot get IDs - -- still tracing multiple logins, appears 1st node is correct, onto 2nd node.. - - - -2012-08-18 (6) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - basic GUI control in browser - debug multiple logins via HTML cannot get IDs - -- tracing join procedure to discover problem - - -2012-08-17 (5) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - basic GUI control in browser - debug multiple via HTML cannot get IDs - -- change join procedure a bit, HTML client only specify vast.io server's IP & port to connect - whereas within the vast.io server, the VON gateway IP/port is specified - -BUG after 3 clients, vast.io server appear to be frozen. - - - -2012-08-16 (4) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - implement client-side vast binding (socket.io based API) - basic GUI control in browser - socket.io-style vast.io interface - -- connect through HTML -> vast.io -> socket.io VAST node server -> VON peer - and get self ID - -BUG: if multiple webpages are opened, seems like it gets messed up, - additional client IDs can be assigned, but not always... - - -2012-08-15 (3) --------------- -goal: build connector to server-side vast node via websocket - debug VON_QUERY forwarding - implement client-side vast binding (socket.io based API) - basic GUI control in browser - -- try to imitate socket.io's implementation to produce vast.io -- study difference between module.exports & exports in node.js - - -2012-08-14 (2) --------------- -goal: build connector to server-side vast node via websocket - implement client-side vast binding - debug VON_QUERY forwarding - -- make vast_voro & LOG work under both browser & node.js environment -- setup GUI environment to integrate with socket.io client-side API - - -2012-08-13 (1) --------------- -goal: build connector to server-side vast node via websocket - implement client-side vast binding - debug VON_QUERY forwarding - self connection as gateway - -- self connection as gateway ok -- finish TODO: - * remove 'id' 'VoronoId' for node.aoi.center when sending outbound messages - - -2012-08-12 (7) --------------- -goal: build connector to server-side vast node via websocket - implement client-side vast binding - debug VON_QUERY forwarding - self connection as gateway - - - -2012-08-11 (6) --------------- -goal: build connector to server-side vast node via websocket - implement client-side vast binding - debug VON_QUERY forwarding - -- try to allow client connect to itself as gateway if it's the first - node to connect - -2012-08-10 (5) --------------- -goal: build connector to server-side vast node via websocket - implement client-side vast binding - -- multiple browser clients can now login and create VON peers - -BUG -- semes like VON_QUERY forwarding is not done correctly, - new nodes aren't getting the VON_NODE messages they need to confirm - joining. - -DEBUG -2012-08-09: cannot bind port continously in VONpeer - main problem is that only net_nodejs can report bind success/failure - but makes more sense to do re-try in vast_net - so need to be careful of who does what - - -2012-08-09 (4) --------------- -goal: build connector to server-side vast node via websocket - implement client-side vast binding - -BUG -- VON peer cannot attempt to bind ports continously (event mishandled) - - -2012-08-08 (3) --------------- -goal: build connector to server-side vast node via websocket - implement server-side vast functions - implement client-side vast binding - -- finish server implement -- rough client version, tested joining success - -BUG -- on second join will join as server still, causing binding to fail & server crash - - - -2012-08-07 (2) --------------- -goal: build connector to server-side vast node via websocket - implement server-side vast functions - -- implement socket.io server join/leave/move functions - - - -2012-08-06 (1) --------------- -goal: build connector to server-side vast node via websocket - -- build a socket.io + VON_peer test client, also define events the client will send - to this "connector" in order to join a VON. - -- add continous port re-binding until successful to vast_net -- add return true/false when listening to port for net_nodejs - - -2012-08-05 (7) --------------- -goal: build websocket test sample - -- run sample socket.io successful, can now run server with node.js @ Windows -- can also send custom message back & forth (sending a message + a js object) - - -2012-08-04 (6) --------------- -goal: porting: VON_peer.js - test _checkNeighborDiscovery() - test _removeNonOverlapped() - -- study how to use websocket - - -2012-08-03 (5) --------------- -goal: porting: VON_peer.js - test _checkNeighborDiscovery() - _removeNonOverlapped() - -- implement all VON functions (pending tests) - - -2012-08-02 (4) --------------- -goal: porting: VON_peer.js - VON_MOVE processing - _checkNeighborDiscovery() - _removeNonOverlapped() - -- notice that when the network has just few nodes, movements are sent via MOVE (not MOVE_B), so meaning that - not explicitly neighbor discovery check will be performed. Need to confirm is this ok behavior. - (possibly okay, as EN will suffice to discovery, only later will boundary neighbor be important as node size increases) - -- do not use 'typeof' to check whether a js object has certain thing (in order to further decode) - use 'hasOwnProperty' to check - -- implement checkNeighborDiscovery() (pending test) - - - - -2012-08-01 (3) --------------- -goal: porting: VON_peer.js - VON_MOVE - _checkNeighborDiscovery() - _removeNonOverlapped() - -- make sure a remote disconnect will generate a proper VON_DISCONNECT -- finish implement move() -- review & move all C++ codebase to js (but comment out non-functional parts) - - - -2012-07-31 (2) --------------- -goal: porting: VON_peer.js - debug same pos no EN issue (still need to join) - -DEBUG -2012-07-30: if two nodes have same position, raymond hill's voronoi produce site for just one node - this impacts neighbor discovery correctness - -- solution: send VON_HELLO & VON_EN separately, so a new node still sends HELLO - even if its EN list for the remote node is empty (can happen if both - nodes occupy the same position, one of them won't get recorded into - the voronoi object, causing inproper judgment) - -- VON_EN works - - -2012-07-30 (1) --------------- -goal: porting: VON_peer.js - test leave(), - debug VON_HELLO - (get_en does not return any neighbor when the first client node joins) - -BUG: -IMPORTANT NOTE: -- found out if two sites are located at the exact same place, - under Raymond Hill's implementation, only one 'cell' will result. - this may have implication for the join procedure, as when a node result its initial - neighbor list via VON_NODE, if just two nodes exist and occupy the same location - only one cell exist and no enclosing neighbor are there. therefore the - 2nd joining node will not be able to contact the first gateway for join - -- implemented VON_HELLO, VON_HELLO_R, VON_BYE -- leave works properly - - -2012-07-29 (7) --------------- -goal: porting: VON_peer.js - test leave(), - debug VON_HELLO - -- modify vast_voro internals so that external queries/checks (get_en, - contains, overlaps) will perform recompute() first and check for - node id correctness, before moving further. - - - -2012-07-28 (6) --------------- -goal: porting: VON_peer.js - test leave(), - debug VON_HELLO - -- try to debug get_en() does not return as expected (mismatch between what's passed - and what's stored: string vs. number) - learn that storing things as keys in a {} map will turn key into 'string' -2012-07-27 (5) --------------- -goal: porting: VON_peer.js - test leave(), - process _new_neighbors array - debug leave undefined target (implement tick()) - -- implement checkNewNeighbors - -BUG -- VON_HELLO does not seem to work correctly when discovering new neighbors - - - -2012-07-26 (4) --------------- -goal: porting: VON_peer.js - test leave(), - process _new_neighbors array - -- implement init data structure, leave() -- make leave work & send neighbors VON_BYE -- right now new neighbors are not processed for VON_NODE, need to fix this. - - -2012-07-25 (3) --------------- -goal: porting: VON_peer.js - test leave(), - allow assignment of new id from gateway - process _new_neighbors array - -- connect success callback added -- change test client to connect to remote gateway -- can get new ID from gateway, at both network layer & VONpeer -- add getID() function to get selfID at vast_net - - - -2012-07-24 (2) --------------- -goal: porting: VON_peer.js - test join(), leave(), - implement VON_QUERY response - allow assignment of new id from gateway - -- join done -- implemented VON_NODE (processing new_neighbors pending) -- add new VAST type: ratio - -2012-07-23 (1) --------------- -goal: porting: VON_peer.js - test join(), leave(), - implement VON_QUERY response - allow assignment of new id from gateway - -- sendNodes seems to work :) -- allow VAST.pos to inherent from point2d (to have 'distance' function) - -BUG: -- isRelevantNeighbor seems to produce different results if overlap - test is chosen to be 'accurate' vs. 'non-accurate'. with the 'accurate' - result not passing overlap test... (a bit strange because a site's position - should overlap with its region). - - -2012-07-22 (7) --------------- -goal: porting: VON_peer.js - test join(), leave(), - implement VON_QUERY response - allow assignment of new id from gateway - -- done porting helpers: - _sendEN - _sendHello - _sendNodes - - -2012-07-21 (6) --------------- -goal: porting: VON_peer.js - test join(), leave(), - implement VON_QUERY response - can determine node id of the remote node sending the message (network-layer?) - -- now when init connection to remote node, connecting node will send its 'id' - first as handshake. the listening node can then learn of the connecting node's id. - however, if no remote id is provided (0, or VAST_ID_UNASSIGNED), then listening - node will assign internal IDs (-1, -2, -3...) to send back messages - - -2012-07-20 (5) --------------- -goal: porting: VON_peer.js - test join(), leave(), - implement VON_QUERY response - can determine node id of the remote node sending the message (network-layer?) - -- exchange node id when connecting (in progress) - - - - -2012-07-19 (4) --------------- -goal: porting: VON_peer.js - test join(), leave(), implement VON_QUERY response - -- finished implement VON_QUERY -- BUG: now enters infinite loop (forwarding request to another node) - bug occurs because from_id is [-1] (cannot yet see remote node's self id) - - - -pending: need to implement: - -_isRelevantNeighbor -_isTimelyNeighbor -_sendNodes -_aoi_buffer - - - -2012-07-18 (3) --------------- -goal: porting: VON_peer.js - test join(), leave(), implement VON_QUERY response - -rename: - IPaddr to addr (vast_addr) - addr to endpoint (vast_endpt) - g_log to LOG - -- implement insertNode() (still in progress) - - -2012-07-17 (2) --------------- -goal: porting: VON_peer.js - test join(), leave(), implement VON_QUERY response - -- make deserialize into VAST objects work for all basic types - - -2012-07-16 (1) --------------- -goal: porting: VON_peer.js - test join(), leave() - -- test out join's send msg to server successful -- in process of implementing VON_QUERY's server response - - -2012-07-15 (7) --------------- -goal: porting: VON_peer.js - join(), leave() - -- implement join() & send message within VON_peer - - -2012-07-14 (6) --------------- -goal: porting: VON_peer.js - join(), leave() - -- add 'disconnect' to vast_net - -BUGFIX: -2012-07-11: vast_nodejs cannot notify for client socket disconnection (can't identify which) - - - -2012-07-13 (5) --------------- -goal: test refactored net_nodejs - -- makes it work (vast_net) -- a problem is that if connection breaks using 'disconnect', - two disconnect events are fired ('end' and 'close') - but if the connection is broken by force (via Ctrl-C) only one disconnect is fired - (only 'close' is fired but not 'end') - - -2012-07-12 (4) --------------- -goal: porting: VON_peer.js - join(), leave() - -- start revising net_nodejs internal implementation (refactor to use less code) - - -2012-07-11 (3) --------------- -goal: porting: VON_peer.js - join(), leave() - - -- extract common data types as a global VAST object (vast_types.js) -- remove requirement to provide a network layer when creating VON_peer - -BUG: -- found out vast_nodejs doesn't seem to be able to notify client socket - disconnection for a listening server - - - -2012-07-10 (2) --------------- -goal: porting: VON_peer.js - join(), leave() - -- a first version, very simple example case of using VAST & VONpeer, test_VON_peer.js - (all includes are available once 'common.js' is required) - - - -2012-07-09 (1) --------------- -goal: porting: VON_peer.js - join(), leave() - -- write constructor test case -- define some common data structures (area, addr, pos, IPaddr, node) - - -2012-07-08 (7) --------------- -goal: implement VON_peer.js - -- done constructor & VON message enumation - - -2012-07-07 (6) --------------- -goal: define VON_peer.js interface - -- done initial version - - -2012-07-06 (5) --------------- -goal: define vast.js interface - -- to design: whether world_id should be inherent to a given gateway - (that is, no need to assign it when joining, the gateway simply setup - a unique world_id) - -- finished defining interface - -2012-07-05 (4) --------------- -goal: implement vast_net.js - test send() & register() - -- remove register(), put as part of constructor - -MILESTONE: -finish first version of vast_net, supporting socket send & listen - - -2012-07-04 (3) --------------- -goal: implement vast_net.js (major net-related functions required by VAST) - -- implemented send(), register(), storeMapping() - - -2012-07-03 (2) --------------- -goal: implement vast_net.js (major net-related functions required by VAST) - -- define vast_net's interface - - -2012-07-02 (1) --------------- -goal: debug net_nodejs.js - -- finished :) - - -2012-07-01 (7) --------------- -goal: implement working net_nodejs.js - -- finish 1st implementation (buggy & not yet working) - - -2012-06-30 (6) --------------- -goal: implement working net_nodejs.js - -- layout net_nodejs main functions, ready to implement - - -2012-06-29 (5) --------------- -goal: layout basic functions / interface for vast_net - (based on c++ version) - -- orgnaized some functions from VASTnet in VAST C++ - - - -2012-06-28 (4) --------------- -goal: finish graphical demo for vast_voro.js (boundary neighbors, AOI-radius) - -MILESTONE: -- done ^^ -- can display enclosing & boundary neighbors (correctly & switchable) - - - -2012-06-27 (3) --------------- -goal: provide a graphical demo of the new vast_voro.js - -- can now detect which id the mouse covers -- can display enclosing neighbors ^^ - - -2012-06-26 (2) --------------- -goal: provide a graphical demo of the new vast_voro.js - -- convert test_voro_rh to use vast_voro.js (works) -- able to display mouse position - - -2012-06-25 (1) --------------- -goal: rh_voronoi: port closest_to() and rest - -- done ^^ - -MILESTONE: -all vast_voro.js methods have been ported with rh_voronoi as the underlying mechanism :) - - -2012-06-24 (7) --------------- -goal: rh_voronoi: port overlaps - debug 2012-06-23 bug: 1st & 2nd level enclosing neighbors are the same - -- done (but havn't checked visually) -- appears that accurate & non-accurate mode of overlap check - produces very different results (so should use accurate mode) - -- also separate each unit test into function forms - - -2012-06-23 (6) --------------- -goal: rh_voronoi: port get_en() - -- done (for 1st level). also optimizes to use simpler procedure - -BUG -- but when going to 2nd level enclosing neighbor, sometimes - both 1st & 2nd level neighbors are the same set. - - -2012-06-22 (5) --------------- -goal: rh_voronoi: port is_enclosing() & get_en() - -- is_enclosing() appears to work correctly :) after calling - getNeighborSet() directly - - -2012-06-21 (4) --------------- -goal: port is_boundary() in rh_voronoi - - -- is_boundary() and enclosed() and both compile and run correctly -- however, found that simply !enclosed() does not necessarily - produce a 'boundary neighbor" -- need other solutions to identify / calculate a boundary neighbor - - -2012-06-20 (3) --------------- -goal: debug contains() in rh_voronoi - -- confirm that the halfedges sent within 'result' of the rh_ovoronoi class, -simply are "edges" forming a circle around thep (with bounding boxes as the end of edges) - -- done insideRegion() ^^ -- replace with a correct & possibly faster algorihtm from: - -http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html - -2012-06-19 (2) --------------- -goal: debug contains() in rh_voronoi - -- check insideRegion() against java version, seems to be the same -- stuck at not knowing how to solve insideRegion()'s correctness - - -2012-06-18 (1) --------------- -goal: debug contains() in sf_voronoi - -line general form: -ax + by + c = 0 - -slope-intercept form: -y = mx + b - -givn two points (x1, y1) (x2, y2) -slope: -m = (y2-y1) / (x2-x1) - -intercept: -(y2-y1)/(x2-x1)*x1 + b = y1 - -y = y1 + [(y2 - y1) / (x2 - x1)]�P(x - x1) -(x2 - x1)�P(y - y1) = (y2 - y1)�P(x - x1) - -(x2�Py - x1�Py) - (x2�Py1 - x1�Py1) = (x�Py2 - x�Py1) - (x1�Py2-x1�Py1) -x2�Py - x1�Py - x2�Py1 + x1�Py1 = x�Py2 - x�Py1 - x1�Py2 + x1�Py1 -(y2-y1)x + (x1-x2)y + (x2�Py1 - x1�Py1 - x1�Py2 + x1�Py1) = 0 - -a = (y2-y1) -b = (x1-x2) -c = (x2�Py1 - x1�Py1 - x1�Py2 + x1�Py1) - -- contains() work with rh_voronoi, but still buggy - (check returns 'true' very often) - - -2012-06-17 (7) --------------- -goal: modify vast_voro to work with sf_voronoi.js - -- finished converting contains & insideRegion (runnable) but buggy - (edges surrounding a cell appears to be all empty) - - -2012-06-16 (6) --------------- -goal: modify vast_voro to work with commonly exposed Voronoi data structures (esp. from rh_voronoi) - first to work with SFVoronoi - -- got sorted sites to expose in vast_voro (from both sf_voronoi & rh_voronoi) -- started to convert contains() (still has errors) - - -2012-06-15 (5) --------------- -goal: modify vast_voro to work with commonly exposed Voronoi data structures (esp. from rh_voronoi) - -RBTree methods: - -rbInsertSuccessor(node, successor) -rbRemoveNode(node) -rbRotateLeft(node) -rbRotateRight(node) -getFirst(node) -getLast(node) - -- calculating basic Voronoi ok by using vast_voro.js - - -2012-06-14 (4) --------------- -goal: modify vast_voro to work with commonly exposed Voronoi data structures - -- found out seg and vertexIndex (used by elements in mEdges) are actually defined & stored in 'line2d' data structure - - - -2012-06-13 (3) --------------- -goal: make vast_voro work with sf_voronoi - -- note what is required from sf_voronoi (used by vast_voro) - -compute(sites, bbox) calculates Voronoi diagram, returns result with edges & cells -get_idx(id) get the index in mSites given an id of the site - -mSites list of voronoi sites (sorted) - site_num id of the site (user-specified ID) - coord position of site - edge_idxlist list of id's of today's working - -mEdges - a,b,c parameters to define a line (edge) - vertexIndex two vertices forming the edge, used by collides() - seg.intersect - bisectingID - -mVertices -result contains a list of calculated results (sorted sites & drawn edges) - - -2012-06-12 (2) --------------- -goal: separate vast_voro from sf_voronoi - -DEBUG: executing functions from sf_voronoi.js from vast_voro.js - causes the required script to be printed out - - > need to 'new' the object before use (otherwise it's treated - as a simple script file) - - -2012-06-11 (1) --------------- -goal: separate vast_voro from sf_voronoi - -DEBUG: infinite loop was caused by min/max x & y values not assigned correctly - -- done - - -2012-06-10 (7) --------------- -goal: separate vast_voro from sf_voronoi - -- revise sf_voronoi.js to behave same as rhill-voronoi-core.js - but when calculating Voronoi, program hangs (infinite loop) - - -2012-06-09 (6) --------------- -goal: separate vast_voro from sf_voronoi - -supported functions: - -insert(id, pos) -remove(id) -update(id, pos) -get(id) -clear() - -contains(id, pos) --> v insideRegion(index, p) -is_boundary(id, pos, radius) --> v enclosed() -is_enclosing(id, center_id) --> get_en() -get_en() --> v getNeighborSet() -overlaps(id, pos, radius, mode) --> v collides() - -needed functions from sf_voronoi (to be used within compute()) - -clear() -insert() -getedges() or recompute() & access to mEdges - - - - - -2012-06-08 (5) --------------- -goal: adopt sf_voronoi to use test_voro_html5.html - -- done ^^ - -2012-06-06 (3) --------------- -goal: test applicability of raymond hill's voronoi implemention to VAST - - -raymond hill functions: - -var sites = [{x:300,y:300}, {x:100,y:100}, {x:200,y:500}, {x:250,y:450}, {x:600,y:150}]; -var bbox = {xl:0, xr:800, yt:0, yb:600}; -result = voronoi.compute(sites, bbox); - -result.edges -result.cells -result.execTime - -edge.lSite -edge.rSite -edge.va -edge.vb - - - -needed functions: - -v insert(id, pos) -v remove(id) -v update(id, pos) -v get(id) - -contains(id, pos) --> insideRegion(index, p) -is_boundary(id, pos, radius) --> enclosed() -is_enclosing(id, center_id) --> get_en() -get_en(id, level) --> getNeighborSet() -overlaps(id, pos, radius, mode) --> collidess -clear - -v closest_to(pos) -getedges() -get_bounding_box() -get_sites() -get_site_edges(id) -getstat() - - - - -2012-06-05 (2) --------------- -goal: convert a generic net layer - -- make sfvoronoi.js usable under both web & node.js - - -2012-06-04 (1) --------------- -goal: make web demo - -[Milestone] - -- finished rough web demo with performance stat record: - - 100 sites 20 - 30ms - 1000 sites 220 - 240ms - 10000 sites 1400ms - -in comparison, raymond hill's version: - - 100 sites < 10ms - 1000 sites 30 - 80ms - 10000 sites 300ms - - - -2012-06-01 (5) --------------- -goal: debug get empty edges problem - -DEBUG: found out it's caused by accessing public variables this.mEdges with "this.mEdges" - (should use l_mEdges when accessing public variables from private functions) - - - - -2012-05-31 (4) --------------- -goal: check sfvoronoi.js against java version - - -MILESTONE: runnable for 1000 sites! :) - -DEBUG: found out some inconsistent assignment between 'null' vs 'undefined' (should use 'null' for some) -BUG: edges are calculated but become empty when retrived later. - - - - -2012-05-30 (3) --------------- -goal: check sfvoronoi.js against java version - -- found bug! - after ELhash is initialized with 'null' crash bug disappear - but appears again if Halfedge is init with certain values as default - -- check till ELinsert() - - -2012-05-29 (2) --------------- -goal: check sfvoronoi.js against java version - -- found bug in Java version! - - in function: - private void clip_line (Edge e) - - y1 = e . c - e . a * x1; - -should be: - - y1 = e.c - e.a * x1; - - -- check through clip_line() - - - -2012-05-28 (1) --------------- -goal: check sfvoronoi.js against java version - -- check till intersect() - - -2012-05-27 (7) --------------- -goal: check sfvoronoi.js against java version - -- handle small bug in getNeighborSet (access to 'edge' is incorrectly placed) -- recompute(), insideRegion() - -2012-05-26 (6) --------------- -goal: check sfvoronoi.js against java version - -- is_enclosing(), get_en() - - -2012-05-25 (5) --------------- -goal: check sfvoronoi.js against java version - -- is_boundary() done - - -2012-05-24 (4) --------------- -goal: check sfvoronoi.js against java version - -- found the 'sites' data struct should contain 'coord' parameter - but currently it's just a 2D pos (but it's ok?) - -2012-05-23 (3) --------------- -goal: check sfvoronoi.js against java version - -- done through intersect() - - - -2012-05-20 (7) --------------- -goal: debug sfvoronoi.js - -DEBUG: when calling nextone() - it should return the sorted sites (from mSites) but currently the - code uses th elements from 'sites' (originally inserted, unsorted). - - also address another potential bug, where the idx2id structure was - used incorrectly (a Hash object, but uses [] to access directly elements) - should use .set .get.. - > fix this by changing to use js array [] directly - - -2012-05-19 (6) --------------- -goal: debug sfvoronoi.js - -- check whether the == check for halfedge object indeeds checks for equivalance. - found out it's okay. will now need to trace from start and see what's causing the - 'null' ELedge - - -2012-05-17 (4) --------------- -goal: debug sfvoronoi.sf - -- modify sites to use just plain javascript {} instead of custom Hash object - (tested ok, but the null ELedge bug still exists) - - -2012-02-21 (2) --------------- -goal: debug sfvoronoi.sf - -MILESTONE: solve a big sfvoronoi.js bug (lasting 4 weeks!) -DEBUG: some pointers appear to be null (cannot access its child members) - > found out it's because the DELETED marker (an Edge object) was changed to null - > but it's actually used for some purpose, so still needs to be in place - - - - -2011-10-24 (1) --------------- -goal: re-build VAST on Linux (redhat) - -got VAST & ACE (6.0.0 download to test machine), had problem building ACE: - -/usr/bin/ld: .shobj/Local_Name_Space.o: relocation R_X86_64_32 against `std::nothrow' can not be used when making a shared object; recompile with -fPIC -.shobj/Local_Name_Space.o: could not read symbols: Bad value - - - - -2011-07-26 (2) --------------- -partitioning methods & approaches - -resizable cells - triangle strips, Voronoi -split-merge - quadtree -micro-cell - squares / triangles - - -VAST.js Goals ------------- -Clean, Fast, Solid - -Support Commercial-grade Network Support -- network monitoring -- transaction logging -- server validation -- secure transaction -- complete knowledge of operations -- comparable user experience -- easy to integrate -- switch between P2P & C/S mode - - - -2011/05/24 (4) --------------- -goal: allow server to listen to port and accept multiple incoming connections - -- allow server to auto-look for available ports after a given port number - - - -2011/05/04 (3) --------------- -goal: define & implement network-layer API - -* what's needed in a 1st version? - -- GUI display to see nodes / Voronoi / movement -- basic VON functions (join / sub / move / list) -- stat collection (for feedback / performance evaluation) - -* considerations: - -- should also consider the ease of porting to other environments (server/web client/Unity client) - -most basic (GenericNetwork) -socket - connect (addr) - send (msg, size) - disconnect (id) - -callback - received (msg, size) - disconnected (id) - -* first target: -a client to establish TCP/IP connection - - - -2011/04/21 (4) --------------- -goal: start VAST.js project :) define network layer - -Needed functions for network layer. Also need to assume ID assignment mechanism exists. - -- store ID-to-addr mapping -- VON send/recv to target -- socket send/recv to target -- get time -- get ID -- get states -- get stats - - - -// basics -start() -stop() -storeMapping(in: id, Addr) -sendMessage(in: target, msg, reliable, headertype) -receiveMessage(out: host, msg) -flush() -process() - -// sockets -openSocket -sendSocket -receiveSocket - -getTimestamp -getIPfromHost - -// state query -isJoined -isPublic -isEntry -isConnected - -// tools -validateIPAddress -validateConnection -tickLogicalClock - -// get/set -setTimestampAdjustment -setBandwidthLimit -getTimestampPerSec -getConnections -addEntries -getEntries -getAddress -getHostAddress -getUniqueID -getHostID - -// stat -getSendSize -getRecvSize -resetTransmissionSize -recordLocalTarget - - - -2011/03/21 (1) --------------- -goal: build VAST with SSL support - -Official Instructions: -http://www.dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ACE-INSTALL.html#sslinstall - -Here's the Linux instructions: -from: http://groups.yahoo.com/group/tao-users/message/19352 - -1) Create $ACE_ROOT/bin/MakeProjectCreator/config/default.features with -the following contents: - -ssl = 1 - -2) cd $TAO_ROOT; $ACE_ROOT/bin/mwc.pl TAOACE.mwc -3) Add "ssl = 1" to $ACE_ROOT/include/makeinclude/platform_macros.GNU. -4) Run make from $TAO_ROOT. - -For Windows: -1) Create $ACE_ROOT/bin/MakeProjectCreator/config/default.features with -the following contents: - -ssl = 1 - -2) Edit $ACE_ROOT/bin/MakeProjectCreator/config/MPC.cfg - with following lines only: - - dynamic_types = $ACE_ROOT/bin/MakeProjectCreator, $?DDS_ROOT/MPC, $?TAO_ROOT/MPC - main_functions = cplusplus:ACE_TMAIN - default_type = vc10 - -3) cd $ACE_ROOT/ace - run $ACE_ROOT/bin/mwc.pl ace.mwc - -4) open the following with VC10 and build project "SSL" - $ACE_ROOT/ace/ace.sln - -Make sure OpenSSL build libraries are installed properly first, can be downloaded here: -http://www.slproweb.com/products/Win32OpenSSL.html - -Note: download the non-light (full) version, so build libraries are included - - -2011/01/05 (3) --------------- -goal: add matcher promotion according to loading - -to-add: -- load detection -- load reporting -- matcher selection - - -2010/12/28 (2) --------------- -goal: upgrade ACE in VAST & build with VC 2010 - -BUG many ACE_OS functions (sleep, hostname...) become unavailable in ACE 6.0.0 (appears that OS.h no longer exists) - -- after searching for functions directly in the ACE source code, found that -need to change include "OS.h" to the following: - -#include "ace/OS_NS_unistd.h" // ACE_OS::sleep -#include "ace/OS_NS_netdb.h" // gethostbyname -#include "ace/OS_NS_arpa_inet.h" // inet_ntoa - - - -2010/12/09 (4) --------------- -goal: sync clocks to gateway for all regular clients - - - -2010/11/23 (2) --------------- -goal: sync clocks to gateway for all regular clients - - -2010/11/03 (3) --------------- -goal: use VAST socket to send/recv JSON BCNet messages - -Milestone: -- get VAST socket working, can establish connection with gateway & send, also receive reply, without - having to first create a VAST node (only VASTnet is needed) - - -2010/10/08 (5) --------------- -goal: refactor VASTnet - -- try to figure out if there's potential thread conflict between incoming / outgoing messages -two levels need to consider: - -1) message queue: inserting and removal of messages of mesage queue, should not affect each other -2) connections: when connecting / disconnecting, the two should not overlap each other - -consider cases: - -1) send & remote_disconnect occurs at the same time -> access to connection object needs mutex - to avoid trying to send, but connection is already invalid - -2) recv & local disconnect occur at same time -> no overlap data structure (should be ok?) - -3) send & local disconnect -> will happen in same thread (no problem) - -4) recv & remote_disconnect -> in same thread (no problem) - -5) disconnect & remote disconnect occur at same time -> access to connection object & handler may conflict - -6) connect & remote connect occur at same time -> duplication connections may result (but will be protected - because socket_connected () & socket_disconnected () will check for duplication - - -2010/10/26 (2) --------------- -goal: VASTnet refactored & tested - -MILESTONE: 1st refactored VASTnet up & running. - -mostly working alright, using VLC found 28 memory leaks, but all are due to use of ACE (reason unknown) - - -2010/10/07 (4) --------------- -goal: refactor VASTnet - -- change definition of timestamp_t from uint32_t to uint64_t, also net_ace's getTimestamp () - returns millisecond time since 1970, instead of the program start, this is so that - * times on synchronized hosts can be somehow compared - * systems running VAST do not require reboot every so often (with uint32_t, can only store millisecond for 50 days) - now it's like 584942417 years :) - - -2010/09/21 (2) --------------- -goal: implement BCNet API based on JSON - -- install JSON with the following steps - * get jsoncpp from http://jsoncpp.sourceforge.net/ - * extract into a path parallel to VAST - * under /makefile/vs71, run jsoncpp.sln and upgrade to VC2008 - * test build lib_json - * include project 'lib_json' into BCNet's solution space - * set lib_json's properties Use of MFC as: "Use MFC in a Shared DLL" - * add to BCNet's include path: "..\jsoncpp-src-0.5.0\include" - * add #include to BCNet.cpp - * add "Json::Value root;" to BCNet.cpp to test creation of JSON objects - - - - -2010/09/14 (2) --------------- -goal: help integrate BCNet with game client - - -DEBUG gateway's response cannot reach the client - > caused by the message is sent to the client's relay component, but target is 'host_id' instead of 'sub_id' - > added a "self-check", if the send target is my hostID then automatically translate - -BUG gateway leaves sometimes will crash (seems like occurs in receiveMessasge for processing, invalid message - is being processed) - - - -2010/09/14 (2) --------------- -goal: prepare BCNet wrapper around VAST for use by BlackCat client - -- compiled the class, also wrote a simple demo_console - -BUG if table server is connected immediately when gatewayConnected () is called, it's okay, - but if the joining to table server is triggered by keypress, it hangs in JOINING stage - - -DEBUG after BCNet is ready & run with demo_console for 1st time, we got "R6025 pure virtual function call" - > found out it's because the callback class was defined as local variable in init () (should be global) - otherwise the instance is destroyed when init () finish execution. - - -DEBUG the BCNet project does not produce BCNet.lib and demo_console could not compile - > the BCNet class was not decleared EXPORT so no lib needs to be produced. - -- found out getIPFromHost () in VASTnet would create multiple additional threads - * when calling ACE_OS::hostname () or ACE_OS::gethostbyname () - * when calling ACE_DEBUG for 1st time - * when listening for TCP port (net_ace_acceptor) - * when listening for UDP port (net_ace_handler) - -2010/09/11 (6) --------------- -goal: separate join procedure into 1) connect to gateway and 2) connect to origin matcher of a particular world - - - - -2010/09/10 (5) --------------- -goal: run VAST in separate thread, define preliminary game API (on top of VAST) - -- VASTThread done, now we can do ticking in separate thread - - - - -2010/09/08 (3) --------------- -goal: run VAST in separate thread, define preliminary game API (on top of VAST) - -BUG: seems like if origin matcher was temporily suspended, restoring it will cause various issues - > possible solution is to let all matchers (active / candidate / origin) continously update their status - > to gateway - -2010/09/07 (2) --------------- -goal: run VAST in separate thread, define preliminary game API (on top of VAST) - - - -2010/09/02 (4) --------------- -goal: build sample program for event-driven VAST - -- simplify 'demo_console' from 'test_console' - -- extend report () into reportGateway () & reportOrigin () - -BUG tick per second in demo_console is only 16 (should be 20, given the setting) - - - -2010/08/20 (5) --------------- -goal: externalize VSO backup matcher maintain - -- finish merging matcher pool for VSOPeer & VASTMatcher, now all matchers are centrally managed by - the gateway node (VSOPeer only queries for new matcher from the VASTMatcher component at the gateway) - -BUG appears that the neighbor list is not up-to-date after Matcher crash & take-over - (if no movement, then late joiner will not know existing node, or deleted nodes unremoved?) - - - -2010/08/16 (1) -------------- -goal: externalize VSO backup matcher maintain - - -BUG after origin matcher crash, some issues observed: - - some clients keep staying in state = JOINING and cannot re-join successfully - - clients seem to be able to remove crashed origin matcher (neighbor list unaffected) - but neighbors are updated only when the client tries to contact the failed matcher. - and after failing, the matcher info is removed - -- need to distinguish between join tasks that only need to do ONCE (creating the object, init variables), - and those that may suceed then fail, and could be performed multiple times during the life of the program - (detecting & finding relays, send subscribe request to matcher and get response correctly). - The former can be done once, the latter may need repeated checking & fallback mechanism - - - - -2010/08/13 (5) -------------- -goal: stress test of multiple rooms - -- perform comparison test as follows: - -condition: -gateway & origin on separate nodes -run time: 6000 steps ~ 10min -join interval: 2 sec. - -* reference run (single room) -100 nodes in single world -runtime ~ 13min -max concurrent 84 -leave recorded: 85 - -* multiple rooms run -100 nodes in groups of 4 (25 worlds) -runtime ~ 13:20 -max concurrent 97 -join recorded: 345 -leave recorded: 96 - -basic findings: running in multiple rooms improve max concurrent, though still some nodes appear -to be unable to join. Client crash however has improved (non crashed, appearantly). but some -may fail to join. - -ToDo: -- test ghost client removal in origin matcher fail (backup takes over) case -- avoid client crash -- ensure client always join successfully if gateway still alive -- gateway / matcher can still function properly after temp network problems -- decentralize VSO gateway function - - -2010/08/12 (4) --------------- -goal: remove client ghosts - -BUG if matcher is not contactable, a joining client may be in JOINING state indefintely - (should somehow switch mode and re-request from gateway, for example) - -DEBUG if a matcher loses origin matcher status due to inactivity, it may re-send keepalive - but its knowledge about origin matcher is still itself, thus may replace the gateway's record, - and make the world to matcher_id id & matchers info inconsistent - > forcefully notify the previous origin that it's not longer the origin - also, would always send a MATCHER_INIT message to either a promoted or demoted origin - -DEBUG client may not be able to send to gateway for any report, if connection is removed - > added check to re-notify the gateway's address, in case of a disconnected gateway - -MAJOR -BUG when the origin matcher is replaced, VSO node still hasn't learned of it, so may when it tries - to promote a new node, the request may be sent to a gateway no longer exist (the departed origin matcher) - somehow the VSO node's idea for gateway should also update, in case of change. - -BUG if a matcher has lost connections to all nodes then resumes it, it tries to re-join to gateway, - but by that time gateway has removed its matcher as well as candidate matcher record, casuing the - matcher unable to join. - -- found that two major cases of fault tolerance needs to be considered: - 1) when nodes permanently fail - 2) when nodes temporaily lose network connection, but then resume - - -2010/08/10 (2) --------------- -goal: debug matcher fault tolerance - -BUG when origin matchers fail, it seems the gateway will lose the origin matcher record for - the failed matcher's world ID. subsequent JOIN request to gateway thus will be incorrect - - - -2010/08/08 (7) --------------- -goal: debug matcher fault tolerance - -DEBUG found that the matcher keepalive check would remove nonresponding matchers, - but subsequent MATCHER_ALIVE would not restore it. - > solved by combining MATCHER_ALIVE with MATCHER_JOINED and remove MATCHER_JOINED, so - each time a keepalive is sent, the effect is similiar to notifying a new matcher - preliminary testing on fault tolerance seems to work okay, except the following bug - -BUG when current matcher disconnects, a client's view of neighbors could also be incorrect - (as no matcher can notify for neighbor deletion) - - -2010/07/29 (4) --------------- -goal: debug matcher fault tolerance - -DEBUG when a origin matcher fails, the backup matcher cannot take over - - sendGatewayMessage in VASTClient would attache sub_id as from field, when gateway tries to - send the new origin matcher info to a VASTClient that sends a JOIN request, as the gateway - doesn't have subID -> hostID mapping, it delivers the NOTIFY_MATCHER to itself - > solved by removing the auto-pending of subID to the client messages' from field - - - -2010/07/22 (4) --------------- -goal: implement dynamic origin matchers - -- the implement direction is to make VSOPeer stay the same as much as possible, while allowing VASTMatcher - to also manage / handle dynamic matcher joining - -- three peer roles in VSO: - * gateway (records all candidates and choose candidate node to join upon request) - * origin (entry point to a particular VSO space - * peer (a regular node on the network) - - -2010/07/21 (3) --------------- -goal: allow worlds to be created dynamically - -- some design thoughts / requirements: - * "origin matcher" for a given world can be spawned on demand by gateway - * all capable nodes should contribute (i.e., can become a "origin matcher"), via registeration at gateway - * VAST node joining includes: - 1) notifying candidacy as potential matchers (including origin) to gateway - 2) obtaining the origin matcher of the joining world - * fault tolerance of "origin matcher" should be supported (i.e., failed matcher is taken over smoothly) - * matchers from different worlds share the underlying "relay mesh" & candidate matchers (i.e., a candidate may support any world) - * of course, everything should be done quickly & smoothly - -- implement thoughts: - - VSOPeer should be as simple as possible (close to current form), "worlds" concept should exist outside it. - - should be easy to 'network' different worlds / gateways (share the relay mesh or matcher candidates) - - -2010/07/20 (2) --------------- -goal: add support of multiple rooms to VAST (add support for optional 'world_id' in join ()) - -- add optional 'world_id' parameter to the join () in VAST interface, so users can choose which "world" to join - - - -2010/07/18 (7) --------------- -goal: 100+ concurrent without bugs - -DEBUG: # of movements is less than expected - > due to too much sleep in between steps, observe that oversleep could occur - for example, if the desired movements / second is 10, so it's 100ms between each step. - If framerate is 20 loops / second, it's on average 1000 / 20 = 50ms / frame. However, - possible that by 20 frames the elapsed time is 97ms, then a movement would not occur - if we're checking if 100ms has passed. A more precise clock and sleep may help - - > fixed by changing the time units used in TimeMonitor from milliseconds to microseconds - - -DEBUG minimal bandwidth size is unusually large in log under Linux - > fixed by replacing printf parameter from %lu to %l (long unsigned to unsigned) - - - -2010/07/16 (5) --------------- -goal: debug to run 500+ concurrent actually - -DEBUG: subscription requests may be sent continously to already failed matcher - > all send to matchers now will go through error check, if matcher send is unsuccessful, - it will be removed & replaced - -- stress performance: 300 nodes, 2 sec / join, concurrent 140 -- 100 nodes, 2 sec / join, concurrent 96 - - -2010/07/15 (4) --------------- -goal: debug to run 500+ concurrent actually - -- will test for 10 minutes runs = 6000 time-steps (10 steps/sec) and 500 nodes, joining at 1 node / sec - (so will take 500 / 60 = 8.3 min to join completely) - - -2010/07/14 (3) --------------- -goal: stress test VAST to 500+ nodes on physical network - -DEBUG: VASTnet::storeMapping (): existing address and new address mismatch. - > issue may be caused when a host accepts new connection from remote host, - it first records the detected remote port (which differs from listen port). Later when the - remote host is registered again, its listen port is used. So the addresses will be different. - > change to if host accepts connection, the remote host's detected port is registered as '0' (not a listen port) - later when listen port is provided (via a RELAY or SUBSCRIBE message), listen port will be used - if the new replacement port is 0 (caused by a disconnected remote host re-connects again), then - it's considered normal. - -DEBUG: when running test gateway, sleep time is always 25ms (40 frames / sec) regardless of load - > found out it's that VastVerse did not actually use TimeMonitor to ensure it runs only within specified time - budget. Updated to do so. - - - -2010/07/13 (2) --------------- -goal: stress test VAST to 100+ nodes on physical network - -05/13 -DEBUG: > gateway shows repeating error message - 'net_ace_handler (): UDP message received, but handler's remote_id not yet known' - - try to run 100 clients, the stat was 230 joins and 43 leaves were recorded by gateway - - > made the handling of the UDP error message to terminate UDP handler, run 30 nodes okay (no more error) - however, there was quite a few following warnings: - - VASTnet::storeMapping (): existing address and new address mismatch. - - also, when all clients leave, gateway still considers one client connected, and there was - repeating subscription request sent to certain node (its current matcher?) - - - -2010/06/23 (3) --------------- -goal: integrate hole-punching to VAST - -DEBUG C# VAST client cannot connect to server, due to SUBSCRIBE message sent to self, would - receive error such as this: - - [13882530681060196353] VASTMatcher::handleMessage () non-Matcher receives Matcher-specific message of type 26 from [13882530681060196353] - - > found out it's because the test program thinks it is a gateway, yet gateway already exists - -DEBUG port is not released even though VAST is shutdown (so init after shutdown does not work) - > found out it's caused by net_ace_acceptor did not call _acceptor.close (); in handle_close () - > a long standing bug addressed :) - - -DEBUG sending a picture of size 63408 bytes would crash the program & gateway - -2010/06/03 (4) --------------- -goal: deply VASTMapChat on SF with a Linux server - -DEBUG seems Win32 client cannot connect to Linux server due to byte alignment problem - the first ID request message cannot be sent / received properly by the Linux server. - it appears that the length of an VASTHeader is 8 bytes (though should be an unsigned int = 4 bytes?) - on Linux, the ID_REQUEST message is 42 bytes, but Win32 is only 34 bytes. Message from Win32 client - is also received with a several seconds delay. - - > found 'unsigned long' in win32 is 4 bytes, but on linux is 8 bytes, here's some comparison: - > adopt a win32-specific "stdint.h" and unix-default and change all types to explicitly specify size - > also changed size_t in VASTTypes.h to vsize_t (defined as uint32_t, as sizeof (size_t) returns 8 bytes in linux - > Win32 clients then can connect properly to Linux server :) - -align: 4 (win32) -======== -sizeof sizes: -VASTheader: 4 id_t: 8 timestamp_t: 4 length_t: 4 coord_t: 4 -Position: 16 Area: 28 IPaddr: 12 Addr: 28 Node: 72 - -transfer sizes: -VASTheader: 4 coord_t: 4 Position: 12 Area: 20 IPaddr: 8 Addr: 20 Node: 52 - -linux: -======== -sizeof sizes: -VASTheader: 8 id_t: 8 timestamp_t: 8 length_t: 8 coord_t: 4 -Position: 24 Area: 48 IPaddr: 24 Addr: 48 Node: 120 - -transfer sizes: -VASTheader: 8 coord_t: 4 Position: 12 Area: 28 IPaddr: 12 Addr: 28 Node: 72 - - - - - -2010/05/29 (6) --------------- -goal: make subID receivable at destination when calling VAST's send () - -CHANGE: -make a significant design change to remove automatically build up fromID -> hostID mapping when receiving -incoming messages. So all ID to hostID mapping now need to be explicit (by calling notifyMapping ()) -The change is so that complex mapping won't be built. Also to avoid VASTnet-level auto-forwarding, which -makes interactions complex & difficult to debug - -DEBUG: after removing the mapping, certain later nodes cannot join properly when regions split, - also, matcher node would see more nodes than AOI neighbors. Suspect it's caused by some bad forwarding. - - > solved by make sure matcher also record the ID->hostID mapping for relays of clients, so that - the SUBSCRIBE_NOTIFY message can be properly delivered. The problem was caused by - SUBSCRIBE_NOTIFY unable to deliver properly and be processed by the matcher itself. - - - - -2010/05/25 (2) --------------- -goal: VAST plugin for Firefox (make join work) - - -MILESTONE: - VAST plugin joins successfully to server.. after creating VAST object into ScriptablePluginObjects - -DEBUG: - Plugin cannot join successfully, also when termintating would crash, found out it's caused by - Plugin being DEBUG mode but VAST.dll is release. If same mode is used for both then there'd be no problem. - - - - -2010/05/20 (4) --------------- -goal: VAST plugin for Firefox. - -Try to build Win32 samples, got samples build (but some would fail first) but cannot be accessed / used correctly. - - - - -2010-05-14 (5) -------------- -goal: debug problem cases - -DEBUG: -- 05/13 in churn test of 50 nodes, recovery from churn is not consistent (between 95% to 99%, depend on cases) -- 05/14 sometimes subscribe is delayed due to slow relay join - -solved by requiring matcher to notify relay directly of the subID -> client host ID mapping, this way -unknown mapping can be minimized, also addressing the above two problems. Test case reveals that consistency -can recover nicely to 99% for up to 10 nodes join/leave / sec for both 50 & 100 stable nodes - - -2010-05-13 (4) --------------- -goal: re-do churn test & finish paper writing - -DEBUG -- 05/13 in churn test of 50 nodes, recovery from churn is not consistent (between 95% to 99%, depend on cases) - > appears to be caused by fast switching over regions, so ownership transfer may not be completed - - found a serious problem where the SUBSCRIBE request is forwarded to neighbor matcher, but currently - matcher responds directly to client (will not reach). Changed to respond via relay, but appears - not all relays have yet the mapping setup - - - - -2010-04-30 (5) --------------- -goal: simulate 1000 nodes - -DEBUG 0.4.4 linux buildable - found out it's caused by the include library order (vastcommon is first, but should be the rightmost as - it's the least dependent) - -- also install a Ubuntu server as Plug server - - -2010-04-29 (4) --------------- -goal: test fault tolerance - -DEBUG -- 04-28 joining is still slow (relay querying) - force a timeout on relay querying (currently set to 10 requests) afterwards we will force - the client to join, even if Vivaldi's error value is not converged yet - -DEBUG gateway hangs when relays exit - - found it's because there may be RELAY_QUERY coming in disconnected clients - - - - - -2010-04-28 (3) --------------- -goal: test fault tolerance - -- added command line flag to determine if this node will add as relay or matcher / none / or both - - - -2010-04-23 (5) --------------- -goal: VAST 0.4.4 release & doc - -DEBUG vastsim_gui shows only '1' as ID - > fixed by building map between node index in VASTsim & subscription ID - - - -2010-04-22 (4) --------------- -goal: VAST 0.4.4 release & doc - -DEBUG node not moving after relay fail, indefintely - > found that it's caused by the node kept trying to contact an already failed relay - > added mechanism to check if the send is successful (if sent to relays) and if not, - remove the failed relays - -DEBUG nodes strangely do not move, without any matcher/relay failure - > caused by the relay disconnects a client, due to inactivity, as all NEIGHBOR messages are - sent directly from matcher to client. - client may need to PING relays periodically. - - - - -2010-04-20 (2) --------------- -goal: real network disconnection test via pskill - - -DEBUG: node stop moving after matcher fail (case 2) - > another problem uncovered was that a re-subscribing client checks in with a newly joined - matcher that does not have the client's subscription record, and thus assigns a new subID. - however, the client program is unaware of the subID change, and thus all future movements - are not accepted by the VAST client component - - fix by remove the check, however, this may be dangerous due to redundent ID assignment - TODO: may need a way to periodically re-check ID with the assigning entry point - - - -2010-04-18 (7) --------------- -goal: debug node stop moving after matcher fail - -DEBUG: node stop moving after matcher fail - > one problem was that when clients try to re-subscribe to new matcher - the new matcher does not consider the re-subscribe client as "owned", and thus - would not send updates to it about its position. - - fixed by forced ownership when accepting SUBSCRIBE request - - another fix was to force notify client of new matcher (MATCHER_NOTIFY) when claiming orphan clients - - - -2010-04-16 (5) --------------- -goal: churn test - -DEBUG: node 24 does not join properly (it can see others after a slow join, but others can't see it) - > slow join is caused by relay pinging each other, causing physical coord to diverge - > due to the slow relay join, SUBSCRIBE cannot yet be sent, and later when re-subscribe - is initiated, layer number 0 and join location (0,0,0) is used. Causing other neighbors - to not receive updates about the joining node. - - Fixed by recording subscription request regardless of whether the client is ready to - subscribe, so later re-subscribe attempt can be correct. - -BUG: seems like after relay only fail, node movement would beomce dragging (one update every two moves) - -BUG: after matcher fail, some clients stop to have movements - -- Perform churn test (allows one node to join / fail per 1 or 2 seconds, with stable size of 30 or 50) - - main finding is that consistency drops with smaller stable size or higher churn rate (50-60%) - with 50 node as stable size and 2 second join/fail rate, consistency may be around 80%. - - two issues found: - - after failure (of relay?) client movement is dragging - - some nodes would stop to move - - both are consistent with existing problem in failure simulations. - - - -- Added JOIN_RATE to VASTsim.ini, to indicate how many steps before a new node joins -- Added STABLE_SIZE to VASTsim.ini, to indicate # of minium nodes to exist before failure can take place - - -2010-04-15 (4) --------------- -goal: finish relay / matcher fail sim - -DEBUG: nasty bug where clients belonging to the 3rd matcher joined would not getting update - > found out it's caused by the MATCHER_NOTIFY message sent by previous matcher to clients, - notifying a new matcher, is sent via relay, but relay only pass through MESSAGE / NEIGHBOR messages. - Changed so that any uncongnized message received by relay is treated as a forwarded message to - client - -BUG: connection size is too large - > possibily too many relays are kept. need to periodically clean up - - -- Added MATCHER_SIZE to VASTsim.ini - -2010-04-13 (2) --------------- -goal: debug relay mechanism - -DEBUG: discover relays do not route properly, problem occurs when relay & matcher exist on - the same node. Matcher may send mssages to relays, which end up is only received by the Matcher's own Client - > route all messages to client to relay first, then it's the relay's task to - forward to clients - > found out it's caused by the relay mechanism not designed properly. Messages sent from - Matcher have subscription ID as destinations. The original idea was to let the network layer - do the subID -> hostID mapping translation. However, as each network layer has only one mapping, - yet proper routing requires two (Matcher -> Relay mapping, Relay -> Client mapping), so it works - if Relay & Client are same node (matcher sends directly to client, in fact bypassing relays), - but if Matcher & Relay are the same node, then the Matcher->Relay mapping turns the message - to itself, but then no mechanism exists to send to Client. - - Resolve the problem by having relay set up a subID -> clientHostID mapping explicitly - (client needs to notify relay of the mapping), and allow the network-layer translation used - only by matcher (for finding relays). Interestingly, NEIGHBOR messages and MATCHER_NOTIFY - (sent by Matcher to Clients) appear can be sent directly without relays, as clients do have - direct connections with their matchers. Other messages such as PUBLISH & SEND need to sent via - relay (as their targets could include clients not managed by the current matcher, so send via - relay is more general). - - -2010-04-12 (1) --------------- -goal: simulate relay & matcher fault - - -BUG: after sure of making relay records are exchanged correctly, now the traffic increases - (from 27 kb / sec to 35 kb / sec without much change in functionality or correctness) - -BUG: if few relays are specified, it seems like some relay join is not successful - and thus their clients will miss the messages targeted at them.. - -BUG: discover relays do not route properly, problem occurs when relay & matcher exist on - the same node. Matcher may send mssages to relays, which hs - - -2010-04-08 (4) --------------- -goal: simulate relay & matcher fault - - -DEBUG: found a rare bug that consistency becomes low (90%) in one case of 30 nodes sim (but all others report 98%) - VAST-case-2010-04-08 node 24 join unsuccessful - - > it's caused by a node not joining properly, as its matcher has just joined and not yet initialized - so the SUBSCRIBE messge was lost. - - fixed by first ensuring there's timeout & re-subscribe even for initial join, and also - allow matchers to queue up receivied messages for later processing when its VSOpeer is still joining. - -DEBUG: client cannot send a message and received by itself, but the matcher node to which it sends, whose client - component could receive the message - in other words, only gateway can receive messages sent by VASTnode->send () - - > found it's caused by VASTMatcher saving up the MESSAGE message type instead of forwarding it - to the client. because when for non-matchers, its VSOpeer will not be created, and thus all - messages forwarded to it is saved (potential problem for non-matchers?) - - solution is to simply allow MESSAGE to be processed at non-matchers - - -2010-04-07 (3) --------------- -goal: simulate relay & matcher fault - -DEBUG: trying to build Linux version for kenny, got this error when linking demo_console - - undefined reference to `vtable - - for the SFVoronoi class, found this explanation: - http://www.daniweb.com/forums/thread114299.html# - - and move the definition of SFVoronoi's destructor to be not inline and solves the linker error - -BUG: linker error, appears to be not specifying the right libraries (however it's not the case...) - -VONPeer.cpp:(.text+0x36bf): undefined reference to `Vast::VoronoiSF::VoronoiSF()' -../../lib/libvast.a(VONPeer.o): In function `Vast::VONPeer::VONPeer(unsigned long long, Vast::VONNetwork*, unsigned long, bool)': -VONPeer.cpp:(.text+0x3917): undefined reference to `Vast::VoronoiSF::VoronoiSF()' -../../lib/libvastnet.a(net_ace.o): In function `Vast::net_ace::receive()': -net_ace.cpp:(.text+0x56d): undefined reference to `Vast::TimeMonitor::getInstance()' -net_ace.cpp:(.text+0x575): undefined reference to `Vast::TimeMonitor::available()' - - - - - -2010-04-06 (2) --------------- -goal: simulate relay & matcher fault - - -MILESTONE: simulate up to 200 nodes (3000 steps) with 96% consistency and 8 kb / sec bandwidth usage - up to 500 nodes (7000 steps) with 94% consistency and 5.1kb / sec bandwidth - -BUG: but occasionally some nodes may stop and not be moving (not sure why) - -BUG: when building linux version, keep getting the following error: - - /usr/bin/ld: cannot find -lACE - - kenny suggests it's because of the static link flag, but the version MMnet provides - is dynamic only - - -2010-04-05 (1) --------------- -goal: simulate relay fault - -DEBUG: found for 30 nodes in standard 768x768 world, still some cases where consistency drops due to - inconsistent views among the matchers. - - - fixed by adjusting matcher's AOI, solves the problem while introducing slight overhead - to matcher AOI radius adjustment - - -2010-03-30 (2) --------------- -goal: experiment matcher fault tolerance - -BUG: found a node would stop getting updates once crossing a region boundary (but not for other nodes) - seems like a new matcher is added afterwards.. - - - it's caused by a complex interaction. The problem is that when subscriber A crosses from - matcher B to matcher C, at matcher C there has actually be some previous ID->host mapping - for subscriber A, such that A is mapped to relay D. What happens is that relay D somehow - disconnects matcher C (timeout?), and matcher C thus removes all mapping reference associated - with relay D (which unfortunately includes subscriber A). Subscriber A's info thus is - removed from matcher C. This removal actually occurs *after* a successful ownership transfer, - so when subscriber A later sends movement updates to matcher C, there will be no processing - & no response. - -MAJOR CHANGE: - - make all time-related tasks consistent to use timestamp for checking - both for countdown or periodic tasks - - getTickPerSecond () changed to getTimestampPerSecond () in VONNetwork - - -2010-03-25 (4) --------------- -goal: debug & test matcher fault tolerance - - - -2010-03-24 (3) --------------- -goal: implement matcher fault tolerance - -- client-side object seems to work more properly when backup-to-closest matcher is used - -BUG: still some ghost objects at client side, suspect due to change in region for the client - -BUG: very poor consistency for 50 nodes under 768x768, as low as 50%! - -BUG: insertion of new matcher seems to jump at locations upon joining - -DEBUG: in gui verion the node ID seem to be redundent - - found out it's because we use subscription ID as ClientID, but clients can get subscription ID from - different matchers, changed to display port number as ID, to show it uniquely in GUI - -DEBUG: clients would 'jump' when cross boundaries, fixed by sending movement to both current & closest matcher - - -2010-03-23 (2) --------------- -goal: finish load balacning - -MILESTONE -load balancing works using the new VSOPeer class. Currently subscription info would only be copied to a neighboring -region if the subscription AOI covers the neighbor region. Found out the most important load balancing design -is to make sure the region center (a VSOPeer node) always follows the center of the load (right now defined as -all 'owned' objects within the VSOPeer's region), this allows the most proper region shape/resizing. -Still an issue is that of the 'ghost objects'.. where replicas still exist though the original has stopped -to send updates to the neighbor region. - -will try use timeout + ping&pong to remove obsolete subscription records (object records in VSOPeer) - -- after removing timeout objects (1 second), ghost objects mostly disappear. However, there are still some - left that do not disappear (possibly due to crossing region boundary so the old matcher has not been - able to notify deletion. - -- nodes also would briefly 'jump' when crossing region boundary, probably due to clients' commands - are not yet sent to the new matcher, so a small inconsistency (pause) while crossing. - (previous VASTATE solution was to send movement to both manager nodes) - -- under 768x768 30 nodes testing, consistency is between 90% - 97%. not very stable. - - -2010-03-15 (1) --------------- -goal: debug simulation layer (make it work) - -BUG seems like if physical coordinates are assigned, they will become NULL soon. (not sure why..) - - - -2010-03-09 (2) --------------- -goal: debug pub/sub - -- notifyMapping would store a different Address object for the same nodeID, caused by - the detected port & actual listening port of a service differ. - - - - -2010-03-02 (2) --------------- -goal: join success - -DEBUG connections seem to terminate abnormally - - found it's because the new message handling method, returning (0) for size would be seen as serious problem - that connection will terminate - -DEBUG packing / unpacking of the Addr object seems incorrect (gets corrupted) when sending RELAY_QUERY message - - - - -2010-02-24 (3) --------------- -goal: linux build for VAST 0.4.3 - -- different cases for incoming messages - (a) ID request - (b) ID assignment - (c) initial handshake (notification of remote host's ID) - (d) regular message - - we now assume if _remote_ID is not known by net_ace_handler, then - it's either (c) or (a) - -2010-02-09 (2) --------------- -goal: pub/sub mechanism done - -Class name changes: -- change VASTNode to VASTClient -- change Topology to VASTRelay -- add VASTMatcher - - -2010-01-28 (4) --------------- -goal: very fast VASTnet layer with topology-aware join & globally unique ID assignment - - -join procedures: - - 1. input a number of entries to VASTnet - 2. connects to a randomly selected entry point - - to get unique ID - - to learn of more relays - 3. concurrent query to relays to find closest relay - 4. connect to closest relay - - -2010-01-26 (2) --------------- -goal: separate JOIN process into (a) join relay (b) join SPS (a VON network) - so that multiple worlds can be supported on top of a single overlay - - -- The relation between MessageHandler and unique ID is now that: - - each host has a globally unique HostID (currently consists of publicIP + privateIP) - - each "relay-level" host can generate a per-world unique NodeID (HostID + variations) - - each host can have several MessageHandlers, and they are addressable via HostID - - MessageQueue supports both HostID and NodeID as transfer target - -steps: - - allow VASTVerse.isLogin () to determine is_public / obtain physical coord with Topology class - - add getHostID () and getNodeID () to IDGenerator - - move use of IDGenerator.getNodeID () into 2nd stage join - - implement VAST.getPhysicalNeighbors () - - implement VAST.getLogicalNeighbors () - - -2010-02-05 (5) --------------- -goal: finish join procedure, begin pub/sub - -- for join procedure, some general steps may be the following - - initiate something (like query for closest relay) - - set timeout and wait - - if reponse is gotten, process, initiate more things - - if timeout, re-send request - -- for join, the basic flow is: - 1. get unique ID & determine whether public IP or not (VASTnet) - 2. determine physical coordinate via ping/pong relays (Topology) - 3. query the physically closest relay (Topology) - 4. contact the physically closest relay (Topology) - 5. query for logically closest manager (VASTNode) - 6. contact logically closest manager (VASTNode) - - if the connected relay fails, or a contact request is not responded within timeout, - another request should be sent to a different target - - - -2010-02-04 (4) --------------- -goal: finish join procedure, begin pub/sub - - -- see that during join there may be two types of routing: - - connect to physically closest "relay" - - connect to logically closest "arbitrator/region manager" - in current design a 'client' connects to physically closest 'relay', but may send/recv pub/sub - messages from its logically closest 'manager'. how to match-up the two efficiently & effectively? - - one possibility is that events / requests are sent to logical managers first, - but updates (bulky messages) are sent via relays back to the subscriber / original requester - - high bandwidth hosts are more suitable as relays - high processing hosts are better as managers - - pub/sub are directed to managers - while message receivals are passed via relays - - -2010-01-21 (4) --------------- -goal: separate JOIN process into (a) join relay (b) join SPS (a VON network) - so that multiple worlds can be supported on top of a single overlay - -- Redesign login process as follows: - 1. a joiner first contact one to several Relays to - (a) learn of other Relays (if it only knows of 1 initial Relay) - (b) obtain its physical coordinate (by querying these known Relays) - (c) connect with the closest Relay and a few neighboring Relays - - at this point, the joiner knows a few physically close Relays - - 2. a joiner then hooks to a VAST network with a gateway server's hostname, this involves: - (a) request joining and pass authenticationi with the VAST network's gateway server - (b) obtain a unique ID that identifies the joiner within this world - - at this point, the joiner is known as a VASTNode and ready to specify subscriptions and publications - - 3. a joiner is considered joined and can perform one of the followings: - (a) subscribe an area of interest (AOI) at a given Layer - (b) perform point or area publications for messages to a given Layer - (c) receive messages sent to the subscribed area(s) at the given Layers - (d) move an existing subscription to a new location - - - -2010-01-19 (2) --------------- - -DEBUG: clients would crash when joining a gateway on experiment on PlaneLab - > found out it crashes at call to ACE_OS::gethostbyname () in net_ace's getIPFromHost () - reason may due to missing dynamic link library for using gethostbyname () - -DEBUG: FLoD gateway cannot send message back to a newly joined VAST node - > found out it's because it tries to send the message via sendMessage (), - but target is a VONpeer (so mapping of VONpeer node ID to hostID still maps back - to the gateway itself). - It means that if a discovered ID is a logical VONpeer id, then it must be addressed - and sent via vastnode's send () (see example usage in demo_chatva). - - - -2010-01-18 (1) --------------- -goal: optimize bandwidth usage (data structure) - -- -BUG: regardless whether timestamp is sent along with MOVE, bandwidth usage is the same - - - -2010-01-15 (5) --------------- -goal: VAST 0.4.2 release - - -- make sure chat_va compiles okay. both DEBUG & RELEASE mode compiles okay - -- change default simulation environment to (basically a Second Life region x 3) - world 768 x 768 - nodes 30 - velocity 3 - AOI 195 - steps 3000 - overload 10 - -basic sim stat - for 30 nodes, 3000 steps, - we eventually use only 5 arbitrators, 98%+ consistency, 14-16 kb /sec bandwidth use - -2010-01-11 (1) --------------- -goal: optimize bandwidth usage (data structure) - -- observe that current load balancing method (moving arbitrator boundaries) would make - arbitrators relatively close to each other (stick at center). need to find better load - balancing policies. - - > seems like if arbitrators are more spread out, then it's better - - > seems like if arbitrator size is relatively fixed (instead of allowing dynamic join / leave) - then it's more stable (better consistency) - - > it appears a "come closer" only arbitrator movement request would make all arbitrators to - move close to each other - -- added OVERLOAD_LIMIT parameter to VASTATE's INI file - -MILESTONE: - > allow arbitrators to chase after the center of agents - created desired load balancing effect (region dynamic re-sizing according to load) - - in a 800x600 space, 90 nodes, 1500 steps, eventually only 15 arbitrators are added - with 92-93%+ consistency - but use 37 kb / sec on average bandwidth - - also the movement is too abrupt - - -2009-12-29 (2) --------------- -goal: remove bad landmark - -BUG: bad landmarks are not reported & removed correctly - - > seems like a terminated program's sockets are still connectable, this also explains - bug 2009-12-28 - > fix by explicitly notify gateway of invalid landmarks to be removed - - -2009-12-28 (1) --------------- -goal: debug physical coordinate determination - -BUG: for 2nd or above nodes joining the system, "no known relay to contact" error occurs - after repeat login/logoff - - > suspect it's caused by relay missing due to failures, yet the failed relays are not - removed propoerly - - - -2009-12-23 (3) --------------- -goal: debug physical coordinate determination - -BUG: can build under Release but stuck on Debug mode for the VAST library - -3> Creating library ..\lib\VAST.lib and object ..\lib\VAST.exp -3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) char * __cdecl ACE_OS::strcpy(char *,char const *)" (__imp_?strcpy@ACE_OS@@YAPADPADPBD@Z) -3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) void __cdecl ACE_OS::thr_yield(void)" (__imp_?thr_yield@ACE_OS@@YAXXZ) -3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) char * __cdecl ACE_OS::inet_ntoa(struct in_addr)" (__imp_?inet_ntoa@ACE_OS@@YAPADUin_addr@@@Z) -3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) struct hostent * __cdecl ACE_OS::gethostbyname(char const *)" (__imp_?gethostbyname@ACE_OS@@YAPAUhostent@@PBD@Z) -3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) int __cdecl ACE_OS::hostname(char *,unsigned int)" (__imp_?hostname@ACE_OS@@YAHPADI@Z) -3>vastnet.lib(net_ace.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) int __cdecl ACE_OS::sleep(class ACE_Time_Value const &)" (__imp_?sleep@ACE_OS@@YAHABVACE_Time_Value@@@Z) -3>common.lib(VASTUtil.obj) : error LNK2001: unresolved external symbol "__declspec(dllimport) class ACE_Time_Value __cdecl ACE_OS::gettimeofday(void)" (__imp_?gettimeofday@ACE_OS@@YA?AVACE_Time_Value@@XZ) -3>..\lib\VAST.dll : fatal error LNK1120: 7 unresolved externals - - > found out it's caused by the following line in net_ace.h (took a whole working day to find out this problem!) - - #define ACE_NO_INLINE 1 - - - -2009-12-15 (2) --------------- -goal: translate latency into physical coordinate - - -BUG - - transmission stat in simulation mode isn't correct (Send < Recv) - - - after destroyNode () is called, if the process holds (waits) then crash occurs - this happens during testing with Actor wrapper - - - -2009-11-29 (7) --------------- -goal: debug simulation mode - -DEBUG: - - all agents stop moving when new arbitrator is inserted, after some time movements resume - > arbitrator doesn't send updates if its is_owner flag isn't set, howerver, during ownership transfer - the flag is not set at all, so no updates are sent. changed so that updates are not sent only - if I'm not owner, but also the object is not in transit: - (is_owner not set, but ALSO in_transit == 0) - - - new arbitrator join would make existing arbitrator leave immediately - > seems like arbitrator ownership transfer isn't complete successfully when new arbitrator joins - transfer occurs but the receiving arbitrator does not know the object - request for object seems to fail / not occurring - - > problem is caused by the arbitrator uses sendAgent () to send full object update to - neighbor arbitrators, but sendAgent () overrides the msggroup to MSG_GROUP_VASTATE_AGENT - and so the arbitrators would never receive the object states properly - - - - -2009-11-11 (3) --------------- -goal: priortized messaging - -* debug message processing (remove FIFO receive queue) - - arbitrator departure's VON_BYE isn't properly received by neighbors - > found out it's because the new storeRawMessage () treats messages with size 0 (VON_BYE contains zero field) - as error and drops the message. - - - - -2009-11-09 (1) --------------- -goal: priortized messaging - -* current problems observed (no cleanup of old object, object reclaim on but no REJOIN message sent to agents) - - arbitrator still claims ghost objects (incorrectly) - - however, agents do not consider them as current arbitrator (existing one not failed) - - therefore arbitrator deletion notice has no effects to outside agents - - extra messages occur for deletion (no message overhead for reclaim) - - currently, consistency under 10 nodes is till 95% + - - - -2009-11-06 (5) --------------- -goal: correct ownership reclaim & no deletion of self agent object - -* note that JOIN procedure of agent & REJOIN may merge - -current JOIN procedure: -1. agent.join (): VASTnode subscribes to AOI -2. publish JOIN event --> received by current arbitrator -3. current arbitrator - - creates agent object (addAgent) - - notifies agent of arbitratorship (ARBITRATOR) -4. agent adds list of arbitrators (learn of CurrentArbitrator) -5. DONE. agent will send future events to CurrentArbitrator - -current TRANSFER procedure: -1. agent moves into another region (MOVEMENT event) -2. current arbitrator transfer ownership to neighbor (TRANSFER) -3. neighbor arbitrator - - accepts ownership (if object doesn't exist, request) - - create agent object (addAgent) - - notifies agent of arbitratorship (ARBITRATOR) - - send back acknowledge (TRANSFER_ACK) -4. original arbitrator removes its in_transit flag -5. if original arbitratrs in_transit is not removed, reclaim ownership - -current REJOIN procedure: -1. arbitrator counts # of ticks an un-owned object is within its region -2. reclaim directly after time-out -3. sends out REJOIN to agent -4. agent sends out JOIN event to current arbitrator - -** step 3 in REJOIN should not occur (let agent detect and send JOIN event by itself) - - - - -2009-11-04 (3) --------------- -goal: add back ownership reclaim while keeping correctness / debug crash - -* run successfully 50, 100 nodes under 800x600 for 1000 steps. (50 nodes 2000 steps are also successful) - problems found: - - - arbitrator may delete an owned agent that it doesn't see in its agent list (the avatar object is thus lost) - - - runs very slowly (too much processing / messages) - - - sometimes the partitioning gets weird (doesn't look Voronoi) - - - - - - -2009-10-30 (5) --------------- -goal: stabalize and correct physical layer VASTATE - - - agents received self deletion message (self disappearance) - > found out it's caused by arbitrator reclaiming ownership for a ghost object (objects it's - no longer receiving updates), and notify the agent that it is its legal arbitrator - (but actually not). The arbitrator then deletes the object (as it's actually outside of - its region, when receiving new update). Thus notifying the agent to perform self deletion. - - current solution is to simply remove the reclaim mechanism.. - - > also added reclaim ownership of in-transit object if acknowledgement is not received in time - - - arbitrators receive exceed EVENTs (continously) - - - JOIN events sometimes fail to receive (50%+ times) - > found out it's because initial subscription area for arbitrator does not cover fully join locations - - - -2009-10-29 (4) --------------- -goal: physical layer VASTATE 50+ nodes - -potential bug: - - agentLogic's self used / referenced before initialization (by SimPeer, but AgentImpl could already call the onXXX () callbacks - which currently assumes the _self variable is valid (though it may not), causing a crash - - > relocate _self variable from SimAgent to AgentLogic, making it a default function, remove setSelf () function - - - agents may become invalid yet unremoved (possibily due to ownership transfer, or in-transit transfer + agent fail) - however, results of sending updates are not checked against such errors - > add sendAgents () function that checks the success of send results and remove any invalid agents - - - object discovery may occur many times at once (possibly requesting many times after receiving - multiple position updates - > record already sent requests and avoid redundency - - - arbitrator receives many events not belonging to it - > it's normal if it's an agent standing close to the arbitrator (as all enclosing arbitrators receive the events too) - - - many pos_version = 0 are detected at gateway - - -2009-10-27 (2) --------------- -goal: debug VASTATE real network deploy (should add 30-50+ concurrent) - -BUG spotted: - - crash for agent when arbitrator insertion occurs - - many object discovery suddenly appear at once (not necessarily bug) - - in pure simulation mode, 100 nodes would cause crash (even 50 nodes would have problems, crash on 1500 steps) - - receive msg on deletion of self from arbitrator - -suspected cause: - - arbitrator subscribes to WORLD, instead of its own region only.. - too many publications / VON maintainance for large node size - - - arbitrator insertion point is not good (too frequent partitioning) - what's the optimal join point? - consider arbitrator MOVE? - - -2009-10-23 (5) --------------- -goal: deploy VASTATE onto Planetlab - -- experimented with inserting arbitrator at mid-way between agent center & arbitrator center - - found that having 10 nodes as threshold is better than 5 nodes (in general) - many instances of agents being notified of self avatar object deletion occurs for 5 node threshold - - but consistency still maintains at over 95%+ if such deletion requests are ignored - - - - -2009-10-19 (1) --------------- -goal: combine VASTATEsim & VASTsim into one library - -to support: - -1) node creation / movement / destruction -2) stat collections -3) rendering / printing of current state - - - - - -2009-10-20 (3) --------------- -goal: debug dynamic arbitrator join - - -MILESTONE: dynamic arbitrator join/leave seems okay (up to 30 nodes tested with 95%+ consistency) - -main problems found were: - - suspected was VON stationary departure (but checked it was working, after adding forced notify after depart detection) - - not all arbitrators join as relays (join process simplified) - - join locations were redundent (check for redundency at each selection, also randomize locations) - - -2009-10-07 (3) --------------- -goal: make dynamic arbitrator join work - -Debug: All of a sudden VASTATEsim doesn't work any more.. cannot even begin simulation, - found out it's caused by addition of VON_EN message, causing message # to exceed 10 and thus overlap with VAST - message #.. (which begin at 10) - - -2009-10-06 (2) --------------- -goal: debug dynamic arbitrator - -added VON_EN message type, so that when VON_DISCONNECT or VON_BYE is received, the receiver may perform a missing neighbor -check with VON_EN. Note that VON_EN is processed as part of VON_HELLO as well. - - - -2009-09-29 (2) --------------- -goal: deploy linux self-runnable linux on PlanetLab - -DEBUG: - after setting export LD_LIBRARY_PATH=./ - the following library error still shows: - - ./demo_console: /usr/local/lib/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./libACE-5.6.3.so) - ./demo_console: /lib/libc.so.6: version `GLIBC_2.8' not found (required by ./libACE-5.6.3.so) - - solved by building ACE directly on older build environment (at MMnet server) - -- compile VAST & ACE successfully at MMNET and build a runnable version - (binary built at Plug server cannot be run because the installed libc version in MMNET is older) - - - -2009-09-24 (4) --------------- -goal: deploy linux self-runnable linux on PlanetLab - - - - -2009-09-07 (1) --------------- -goal: debug re-join problem (cannot re-join after node fail) in real network - port to Linux system - - - -2009-09-04 (5) -============== -goal: debug VASTATE integrated approach - -- by copying events to also enclosing arbitrators (but only when crossing boundary), we're able - to transit over arbitrator boundary without the 'jumping' in position. Likely because - the event was immediately effective after the ownership transfer. - -- "lean back" is now supported by sending object updates to only the closest enclosing arbitrator - to an object. This way all objects have only ONE backup. However, this approach requires - that arbitrators also have a request-based mechanism similiar to what agents have in - requesting from a neighbor arbitrator the full object states if it receives only partial updates - -MILESTONE: integrated approach works & tested (10 nodes up to 10 arbitrators seem correct) - - - - -2009-08-27 -========== -goal: optimize & debug VASTATE - - -DEBUG solved one self disapperance bug (10 nodes, 5 arbs) - ownership transfer okay, agent also gets new current arbitrator notification. - Problem occurs as there was a previous mapping of the new arbitrator (but to - the incorrect host), due to a previously forwarded QUERY request (as it was - forwarded from gateway, the agent records the arbitrator as map to gateway. - All subsequent events therefore would not reach the new arbitrator. - - Solved by calling notifyMapping () whenever a new ARBITRATOR is received by an agent - -DEBUG solved a potentially nasty bug where agent movement across arbitrators is correct - under DEBUG mode but incorrect (agent stops moving) in RELEASE mode. when - # of arbitrators == 5 (under 10 nodes) - - observe that the agent IDs are different in debug & release. Therefore required - arbitrator be created FIRST before agent is created. then solved the problem - (same agent ID & behavior for both DEBUG / RELEASE mode) - -DEBUG solved another disapperance bug, caused by the new arbitrator not knowing how - to contact a new agent within its region. solved by calling notifyMapping () - - -2009-08-21 -========== -goal: debug self disappearance in multiple arbitrators - -DEBUG: objects near & outside of AOI boundary keep appearing & disappearing - Caused by object deleting occurred while still receiving update publications (so agent would request - the object again). - - Solved by setting two AOI buffer zone, a closer one for notifying agent discovery / disappearance - (onCreate and onDestroy), another further one for actually deleting the object, and a furthest one for - subscriptions. (so with the define VASTATE_BUFFER_RATIO as the zone to remove, VASTATE_BUFFER_RATIO/2 as - the zone to notify agent, and VASTATE_BUFFER_RATIO*2 for the subscription area). - - -2009-08-18 -========== -goal: debug VASTATE attribute update bug in real network - debug self disappearance in multiple arbitrators - - -DEBUG: found a bug of unable to start simulation after 2nd node. - problem was caused by agent trying to join (subscribe AOI & send JOIN event) - before the arbitrator on the same node has joined (yet the arbitrator - manages its region). So this creates a case that the agent's JOIN event was - never processed by a valid arbitrator. - - - -2009-08-12 -========== -goal: test the new "discovery by request" concept - -MILESTONE: -successfully tested "discovery by request" as a valid way for agents to find objects -without arbitrators having to backup more than neighboring arbitrator objects :) - - -2009-08-11 -========== -goal: debug agent-side object discovery bug - -- document two important bug cases in VASTATE-case-2009-08-11 - that includes - 1) inproper discovery due to incorrect arbitrator knowledge - 2) undiscovery caused by AOI spanning across more than two - arbitrators' regions. - - - - -2009-08-09 (7) --------------- -goal: debug ownership reclaim - -DEBUG - - found out first problem in reclaim is that the node in VASTATE may not have failed properly (added calls to leave () - for both arbitrator & agent when a VASTATE node fails - - - found out another problem is that when ownership for avatar object is reclaimed, the "agent" info is not re-created, - thus the arbitrator may consider the avatar object's agent disconnected and destroyed the avatar object - (addressed by sending ARBITRATOR to the disconnected agent with a flag, after which the agent would send back - the proper TRANSFER to the new arbitrator) - - - - - -2009-08-07 (5) --------------- -goal: debug ownership reclaim - - - -2009-08-05 (3) --------------- -goal: implement arbitrator fault-tolerance - -- realize that proper arbitrator fault-tolerance rests on - 1) proper notification of object creation/existence and ownership transfer - 2) assume ownerships of unowned objects in case of neighbor failure - - and it's important to clarify / define behaviors for an arbitrator when - 1) a new neighbor arbitrator is learned - 2) an existing neighbor arbitrator has departed (or become non-enclosing neighbors) - - arbitrators should also notify enclosing neighbors (or all known neighbors?) when - 1) it creates / updates / deletes an object (to all) - 2) it transfers ownership to a neighbor (to target only) - 3) it acknowledges ownership transfer (to target only) - - right now arbitrators process messages only if it's from an agent whose avatar object it owns - (regardless whether the agent is inside its region or not) - - ownership according to region containment can be seen as a 'soft-state' decision - (i.e., ownership is not 100% according to region division, as there could exist temporary inconsistency) - - -- methods to take when - 1) a new arbitrator is learned - a. send current list of owned objects - b. new arbitrator will check against its own and request for any unknown object - c. send new arbitrator missing objects in full - - this will help a new arbitrator to learn fully enclosing neighbors managed objects, - without receiving redundent full object updates for those already known - - 2) a known arbitrator has departed - a. send list of owned objects - b. departed arbitrator will mark those objects as deleted - - - -RELEASE 0.4.0 (file: VAST-c++-0.4.0-src.7z size: 4,479,687) - -This is a major release of VAST, re-adapting the original support of VON -(Voronoi-based Overlay Network) to the support of SPS (Spatial Publish Subscribe). - -Details of VON and SPS can be found in the following papers: - -VON: http://vast.sourceforge.net/docs/pub/2006-hu-VON.pdf -SPS: http://vast.sourceforge.net/docs/pub/2009-MMVE-SPS.pdf - -For other details please see "README.txt" for general descriptions and -"INSTALL.txt" for how to use VAST - - - -2009-08-04 (2) --------------- -goal: debug 2nd node join bug in chatva, release VAST 0.4.0 - -DEBUG -2009-08-03 chatva 2nd node+ does not move properly (after some pause, 3rd node doesn't move at all), - also cannot display chat messages except 1st node - - found out it's because the VONpeer is initialized with the local host's time (which may be more - advanced), causing subsequent movement updates cannot reflect to the VONpeer by the original Client host. - (unless its logical clock has advanced enough to exceed the Relay's logical clock at the VONpeer's - creation). Solved by initializing VONpeer's logical clock as 0. - - -DEBUG another bug following the above is that chat msg from 2nd node is shown only at 1st node. - found it's caused by adding 'send time' to the PUBLISH message (to measure latency). - However, as when processing MESSAGE, it's sent to local nodes first before remote nodes, - so the sendtime was extracted, and thus the message sent to remote node isn't correct. - Solved by reversing the order to send messages - - -2009-08-03 (1) --------------- -goal: implement fault tolerance for VASTATE arbitrator - - - -2009-07-30 (4) --------------- -goal: debug ownership transfer (over 4+ arbitrators) - -DEBUG -2009-07-29 seems like an arbitrator that gets a transferred object would try to delete it at some point - - caused because the agent info is not also transferred, so the new owner does not think - as a valid avatar objects for one of its peers and will consider it as a disconnected agent - (thus removing it) - - transfer agent info as well - - -MILESTONE: ownership transfer under multiple arbitrators success (up to 5) - - did two things to address bugs: - - transfer agent info along with ownership - - upon receiving ownership transfer, also notifies neighbor arbitrators of object creation - (so that subsequent ownership transfer can work correctly) - - - - -2009-07-29 (3) --------------- -goal: debug ownership transfer - - -- improve the joinRelay () mechanism in VAST. -- agent in VASTATE now also would check for redundent neighbor INSERT (inserting existing, known neighbors) - - -2009-07-27 (1) --------------- -goal: implement ownership transfer - -- use Visual Leak Detector (VLD) and found memory leakages for VAST, removed some, - but pointers to a the same newed object seem to be counted as leakage even though - the object would be deleted at some point - - - - -2009-07-26 (7) --------------- -goal: debug update publication & implement ownership transfer - -DEBUG: update publication debugged (07-23 bug) - -2009-07-23 for more than 1 arbitrator (2), the second arbitrator's agents do not seem - to correctly get position updates for agents managed by the 2nd arbitrator - except when just discovering at boundary (then no updates). Appearantly - the arbitrator still updates & keeps track of agent positions correctly, - just that updates are not correctly published / received by agents - (discovery works because it's a one-to-one communication). So seems like - something's wrong with the 2nd arbitrator's publication mechanism. - - later found it's not just 2nd arbitrator, 1st as well. - - found out it's caused by msggroup being filled incorrectly. - for publiation via a vastnode, msggroup should be MSG_GROUP_VAST_RELAY - for updates sent to neighbor arbitrators directly, msggroup sould be MSG_GROUP_VASTATE_ARBITRATOR - the problem occurs when the same message is filled as MSG_GROUP_VASTATE_ARBITRATOR, - but then sent via vastnode (so it cannot be processed by the correct relay). - - re-written the Message.clear () so that msggroup is clear up each time - - - - -2009-07-25 (6) --------------- -goal: debug arbitrator publication to agents, ownership transfer - - - - -2009-07-24 (5) --------------- -goal: object & ownership transfer among arbitrators - - -2009-07-24 appearantly the arbitrator & agent in VASTATE would join at different physical coordinates for same host - this seems to be caused by Valaldi giving synthetic coordintes based on host_id (arbitrator & agent have different - host_ids) - - partial fix: valvidi currently assigns the same physical coord to two consequentive hosts (e.g. 1&2, 3&4) - -2009-07-24 relay join cannot succeed for the 3rd node under ENABLE_LATENCY for VASTATEsim - preliminary investigation shows it's caused by the physical coordinate of node 2's agent /arbitrator - has resorted back to the movement coord instead of physical coords given by Valvidi - - found out it's caused by Agent's join () updating the _self's position, which happens to be the - a pointer to the vastnode's position (which should be a physical coord used by the VASTnode to join) - thus corrupting.. this means that getSelf for Agent and Arbitrator should mean different things - (acutally.. both should be the logical coord, while the vastnode's coord is protected from external - corruption). Solved by having separate _self variable maintained within both AgentImpl and ArbitratorImpl - indepedent from the _self variable in vastnode or VONpeer - - - -2009-07-23 (4) --------------- -goal: object & ownership transfer among arbitrators - - - - - - -2009-07-22 (3) --------------- -goal: agent can call getArbitrator () and obtain current arbitrator info - -DEBUG: -2009-07-22 certain position updates do not seem to take effect in VASTATE (esp. when the node is at boundary) - - solved by using an AOI-buffer multiplier for the agent's subscription area. - appearantly the bug is caused by the agent not receiving updates sufficiently for - border objects - -- multiple arbitrators join a VON success, can take events and send updates. - however, currently all arbitrators subscribe the same area for events, and create avatar objects - for only those users within their regions (a bug exists that clients do not see other users created - by neighboring arbitrators). - -DEBUG: found out the reason why a 2nd arbitrator was not able to join a VON is because of message - ID collison. the VON messages are in numbers between 1 - 10, so other message handlers - intending to use VON internally and helping to relay VON messages (such as Relay class and now - Arbitrator) should define their message number starting 10 + . - - - - - -2009-07-18 (6) --------------- -goal: debug peer target not found after node fail - - -DEBUG peer target not found, found out it's caused by the failed node - - - not sending explicit VON_BYE to all existing neighbors - - not sending explicit VON_BYE to potential neighbors (who might have accepted HELLO already) - -DEBUG -2009-06-29 in VAST, some "ghost nodes" sometimes exist when # of node size is large (~50) - - caused by receiving outdated NODE messages - solution: use explicit VON_HELLO acknowledgement (handshake before recongnzing a neighbor) - - -2009-07-09 (4) --------------- -goal: support debug & integration (known bugs: join may not succeed for 2nd node) - -- add join timeout for VAST node to re-send QUERY message to gateway - - - - -2009-07-08 (3) --------------- -goal: debug large object creation crash & join not exceeding 3 - -- found out large object creation (100 objects, 1000 attributes, 9 MB of objects created at - a time) or frequent movements do not cause crash, even with ACE. However, if arbitrator - allocates more than 1.7 G RAM (real & virtual) then crash occurs due to memory allocation - error. - - - - - -2009/07/07 (2) --------------- -goal: debug crash bug when peer disconnects - -DEBUG: solved the long-standing crash bug when an agent leaves, other existing agents - would crash - - found the issue was caused by ACE printing error message about not - being able to re-connect. After removing the invalid debug parameters %p the - problem was solved - -DEBUG: after an agent successfully disconnects, other existing agents would temporily - pause upon movement once in a while. - - it's caused by not clearning VONPeer for the disconnecting agent cleanly, - so existing agents still try to send VON movements to it. - - solved by sending the VON_DISCONNECT properly - - - - - - - -2009/07/01 (3) --------------- -goal: test multiple arbitrator's JOIN to VON & SUBSCRIBE - -- tested up to 4 nodes in real network (chatva) that shows correct VON behavior - - - - -2009/06/29 (1) --------------- -goal: debug VAST's consistency issues - -DEBUG: -2009-06-12 in VASTsim neighbor position updates sometimes are lost, or jump abruptly - likely caused by timestamp issues - - found out some small delay in position update is caused by position-only updates - (vs. full update) does not have timestamp, so outdated updates are treated the same - as new updates. Add timestamp to incremental position updates - - another issue is caused by the removeNonOverlapped policy, where the drop count is - checked only every MAX_DROP_COUNT times, and would reset to 0 if the relevance check - passes. So it's possible that a drop count exceeds the limit but the neighbor has - just become irrelevant. This makes too short a time to disconnect irrelevant neighbor. - re-design the check so that an irrelevant neighbor must remain so for MAX_DROP_COUNT - ticks before being removed. - - also found that the AOI_BUFFER setting, should use a resonably large value - (increased from 10 to 20). Also when the receiver of NODE message try to decide - if to accept the notification, it should be more tolerant (use AOI_BUFFER * 2 as - relevance check buffer) - - some simple stats: - - with 10 nodes, - - AOI-buffer Consistency per sec U/L connected neighbor AOI neighbor - 15 99.9620% 3235/3231 - 17 99.9848% 3239/3236 - 20 100.0000% 3246/3242 5.3 ~ 6.3 1.8 ~ 3.8 - - with 50 nodes - 15 99.8485% 9684/9674 - 20 99.9928% 10087/10076 14.4 ~ 16.9 11.3 ~ 12.8 - - - -2009/06/27 (6) --------------- -goal: debug VASTATEsim (join, move) - - -DEBUG: when gateway replies to LOGIN request by an agent, it cannot find the receiver's ID->address mapping, - as the agent may use a previously existing connection (with unassigned ID). - -NOTE for each SimPeer (in VASTATEsim), two hostIDs are allocated (one for agent, one for arbitrator), - however, ID assignment may occur in any order (agent or arbitrator may obtain unique ID sooner than another) - across different SimPeers - - -BIG BUG: - when agents join VASTATE, they may publish JOIN event to be received by an arbitrator. - However, as agents and arbitrators have different AOI (subscription areas) an agent's AOI - may not contain the arbitrator, and thus will not know the presence of the arbitrator to publish - the JOIN event. - - solution seems to be to support mutual discovery in the VON layer (i.e., A is notified of B not only if - A covers B, but also if B covers A). - -MILESTONE - -VASTATEsim_console seems to be working, after fixing the above Big Bug, by changing the definition of isRelevantNeighbor () -in VONPeer to be of mutual interest instead of just node A's AOI covers node B. -seems like LOGIN, JOIN, and MOVE are working using VASTATE... - -- also try to add cleanConnections () in VAST, but havn't gotten the right way to do it.. - - - - -2009/06/26 (5) --------------- -goal: debug VASTATEsim (join, move) - - -found out the initial JOIN publication of a joining agent cannot be received by arbitrator -as the agent has not yet subscribed AOI properly (i.e., not a member of the VAST network -to send valid publications). - - - - -2009/06/25 (4) --------------- -goal: debug VASTATEsim (join, move) - -DEBUG -2009-06-25 calling publish () would crash demo_chatva, detailed trace shows that - using addTarget () inside publish () corrupts the dellocation of Message object - likely issues are caused elsewhere - - found out it's caused by instancing a Message object in chatva and passed into - VAST, when VAST adds a target it allocates memory internally, which cannot - be de-allocated in chatva (outside of VAST). Temp solution: create another - internal copy of the message to manipulate. - - - - - -2009/06/19 (5) --------------- -goal: debug VASTATE funcions (create/update/destroy objects) - - - -- makes publish () works for VAST (point publication with layer stored as the 'pad' field inside - IPaddr - - - - -2009/06/18 (4) --------------- -goal: debug VASTATE funcions (create/update/destroy objects) - - -DEBUG: -in VAST when using chatva and only a single relay (client-server mode), when peers leave the -info are still retained. - -found out it's because the VON_DISCONNECT messages have not been properly sent to still -existing peers. Also add the removePeer () function in Relay.cpp - - -2009/06/17 (3) --------------- -goal: implement VASTATE funcions (create/update/destroy objects) - - -A note on current ID usage / generation - -VASTVerse - maintains host_id -MessageQueue - maintains host_id - -VONPeer - always externally supplied (including gateway) - -VAST - equals host_id, differentiable with msggroup -VASTATE - none (only factory class) - VONPeer - for arbitrators, obtained via getUniqueID (locally generated) - Agent - host_id + msggroup ? - Arbitrator - host_id + msggroup ? - - Agent-VAST - - Arbitrator-VAST - - - - -2009/06/12 (5) --------------- -goal: debug VAST's real network layer - - -found UDP is causing the disconnection, temporarily use TCP for movmment updates - - - -2009/06/11 (4) --------------- -goal: debug new VAST architecture (messagehandler) - -* MILESTONE: -VAST works in new architecture. simulation works for 50 nodes in 50 relays or 1 relay. -Can see obviously centralized approach uses slightly less bandwidth.. (as expected :) -real network can connect, but still buggy (okay up to 3 nodes) - -DEBUG: -2009-06-11 non-relay VONPeers do not properly receive neighbor movement updates via their relays - as only a single MOVE is sent if multiple targets exist for the same host, - at the handling side, the same received message is passed to multiple VONPeers several times. - This causes issues in content extraction, where after each pass we need to 'reset' - the current pointer in the content - - -DEBUG: -2009-06-10 no global node position for non-relay nodes - caused by ambiguiy of hostID & VONPeer ID. So when a VONPeer 7 sends a message to VONPeer 2 - via VONPeer 1 (host 1). At host 1, it's unclear whether the message should be handled by - host 1, or host 2. As there can only be one mapping between an ID and a host (id2host mapping) - - solve this issue by separate hostID & VONPeer ID, such that VONPeer ID is now running - in the reserved bits of a hostID (the most significant X bits). this will target to host - mapping would be clear. Also, remove the strict check of destination ID before processing - (now when process messages, a message is locally handled as long as the message is not - for forwarding (no id -> host translation shows a different host) and a local handler - for the message group can be found. This provides flexibility for VONPeer messages - be first sent to a Relay message handler. - - result is when now non-relay nodes also can learn of neighbors, but only at 66% consistency - - - - -2009/06/10 (3) --------------- -goal: debug new VAST architecture (messagehandler) - -BUG: -- 2009-06-10 normal operations if all nodes are relays, but no global node position for - non-relay nodes & redundent NEIGHBOR messages / notifications (duplicates of neighbors) - probably caused by VONPeer not properly created at a relay - -DEBUG: -join bug caused by - * too short an interval time to determine timely neighbors - (thus a new peer doesn't accept info from VON_NODE message) - * not ticking each VONPeer (during Relay's postHandle ()), therefore - HELLO & handshake are never sent - - -2009/05/15 (5) --------------- -goal: real network with 30-40 nodes - - -DEBUG: -allow failed node to be removed from neighbor list by recording node's last active time -and remove non-active neighbors after some time (also add sending keepalive MOVE messages -periodically) - - - -2009/05/14 (4) --------------- -goal: real network with 30-40 nodes - -DEBUG (after node fail simulation ceases to work) -- other nodes still move after node fail, caused by improper handling of DISCONNECT message - (generation and processing of the DISCONNECT message) - - -2009/05/13 (3) --------------- -goal: debug real network - -MILESTONE: -- real network works for 1st time (3-4 nodes :) - - -2009/05/12 (2) --------------- -goal: debug real network layer - -- code real network -- send / receive tested with SimNode - - -2009/05/08 (5) --------------- -goal: debug migration and test on real network - - -2009/05/07 (4) --------------- -goal: code on peer migration (fault tolerance of relay) - - -- separate MOVE & MOVE_B into MOVE, MOVE_F, MOVE_B, MOVE_FB, where F indicates full AOI info - tested successfully with reduced message overhead - -- make metric collection work (topology consistency, bandwidth, etc.) - -DEBUG: -2009-05-07 send only position update seems to cause topology inconsistency - caused by undefined assignment operator for classes such as Position/Area - so we would update empty positions or AOIs for neighbors - - -2009/05/06 (3) --------------- -goal: debug relay->client message - code on peer migration (fault tolerance of relay) - - -- correct run of relay -> client messages - - - -2009/05/05 (2) --------------- -goal: optimize relay -> client messages (position updates) - correct stat messages (consistency / avg. bandwidth) - -BUG: -- notify Client of a Peer's neighbors incrementally (but buggy) - - - -2009/04/30 (4) --------------- -goal: debug VAST, have VASTsim_gui running - -DEBUG: -VASTBuffer did not initilize its internal buffer size to the allocated one -(which might cause inefficiency in double-allocating every time) - -DEBUG: -the ID request sent from 2nd Client to 1st Gateway does not decode properly. - -found out receiveMessage does not treat the received message as a combined message -(each with individual total_size in front), this is due to how net_ace & net_emu -treat received messages differently (ACE strip out the total_size upon receiving -while net_emu doesn't). - -DEBUG -- for the initial VON_QUERY message, cannot put the 1st relay (gateway) as the contact - address, as the gateway will not have the message handler for the sender node. - solved this by including both the "joiner" and "relay" info in the query message - -MILESTONE: -VASTsim_console runnable at 4:22pm :) -VASTsim_gui working at 5:00pm :) - - - -2009/04/29 (3) --------------- -goal: VASTsim converted - 1st VASTsim_gui running - -BUG: -serialize & deserialize have different results -DEBUG: -found out it's because after deserialization in Message, the _curr pointer did -not return to 0, so the subsequent calls to extract () were incorrect - - -DEBUG -- add send self messages (route to receive buffer directly) - - - -2009/04/28 (2) --------------- -goal: VON procedure MOVE - start to run simulation with VASTsim - - -- MOVE done, as well as JOIN, SUBSCRIBE for Peers -- eliminate the Coord class from Movement.h in VASTsim - - - - -2009/04/24 (5) --------------- -goal: client & relay join cleanup - VON procedures - - - -2009/04/23 (4) --------------- -goal: client & relay joining - -- initial steps for joining (client contacts VAST node at gateway, which rely with the - closest relay info, client then contacts the relay directly to request join) - - -2009/04/22 (3) --------------- -goal: creation of Client and Relay nodes - -DEBUG -- serialize / deserialize methods in VASTTypes - virtual functions add 4 bytes to the sizeof () operator to a class, - so inherentance a Serializable class would cause problems when using purely sizeof () and - memcpy (this...) to serialize the class. - - re-implement all serialize (), deserialize () methods for Serializable classes - -IMPORTANT: -- byte-aligned all basic types to 8 bytes, which is VC's default - - -2009/04/21 (2) --------------- -goal: correct id2host mapping - VAST check whole process, - -- id2host mapping done - - - -2009/04/18 (6) --------------- -goal: correct id2host mapping - VAST check whole process, - -- work on FLoD-IC draft - - -2009/04/17 (5) --------------- -goal: VAST check whole process, - Relay, Topology class implementation - -- remove Relay class (realize it can be implemented in the main VAST class - for performing both Client and Relay tasks) - -- unique ID assignment / login procedure done -- find out id2host mapping in MessageQueue needs to be implemented and is important - - - -2009/04/16 (4) --------------- -goal: unique ID assignment, VAST implementation - -- finished unique ID assignment, -- compiled first VAST library - -DEBUG -- many linker errors at first, resolve by building VASTnet & common as static libraries - then errors occur as MFC libraries are staticially linked in both VASTnet & common - projects. Resolved by linking MFC dynamically as DLL. - Finally resolve linker problems by not using any netemu_bl classes (not converted yet) - - -2009/04/15 (3) --------------- -goal: VASTnet refactor, - unique ID assignment - - -- try out SVN and backup everything to it (both VAST, FLoD & Plug) -- can compile VASTnet except net_emu_bl - - - -2009/04/13 (1) --------------- -goal: finish VASTnet refactor - finish VAST modificiation (area subscription, point publication) - -- consolidate various VASTnet classes. compiled most of them now. - - - -2009/04/12 (7) --------------- -goal: revise VASTnet & message handler mechanism - (support easy logical node implementation & migration) - -- realize nodeID + messageType would be enough for unique handler identification - no need for GroupID or HandlerID as MessageQueue can do the necessary mapping - lookup - -- also connection/disconnection from VASTnet can be removed, just use a notify - function for storing nodeID -> address mapping and timeout for removing - unused connections - - - -2009/04/10 (5) --------------- -goal: debug & revise VASTnet interface / data structures (compilable) - - -2009/04/09 (4) --------------- -goal: define all interface and data structures & figure out how they work together - -CHANGE: -separate existing classes into VAST, VASTsim, VASTnet, common libraries - -BUG: when building VASTnet, all files compile but generate the following linker error - -net_ace.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __thiscall ACE_Thread_Mutex::ACE_Thread_Mutex(wchar_t const *,struct ACE_mutexattr_t *)" (__imp_??0ACE_Thread_Mutex@@QAE@PB_WPAUACE_mutexattr_t@@@Z) referenced in function "public: __thiscall Vast::net_ace::net_ace(int)" (??0net_ace@Vast@@QAE@H@Z) -net_ace_handler.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall ACE_Thread_Mutex::ACE_Thread_Mutex(wchar_t const *,struct ACE_mutexattr_t *)" (__imp_??0ACE_Thread_Mutex@@QAE@PB_WPAUACE_mutexattr_t@@@Z) -net_ace.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::init(int,wchar_t * * const)" (?init@ACE_Shared_Object@@UAEHHQAPA_W@Z) -net_ace_handler.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::init(int,wchar_t * * const)" (?init@ACE_Shared_Object@@UAEHHQAPA_W@Z) -net_ace.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::info(wchar_t * *,unsigned int)const " (?info@ACE_Shared_Object@@UBEHPAPA_WI@Z) -net_ace_handler.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall ACE_Shared_Object::info(wchar_t * *,unsigned int)const " (?info@ACE_Shared_Object@@UBEHPAPA_WI@Z) - -DEBUG: -Found it's because ACE was built with VC7, after building new ACE with VC9 it gets fixed. - - -2009/04/08 (3) --------------- -goal: define data structures - -- completed half, defined unique ID format, simplify MessageID (remove handlerID concept) - - -2009/04/03 (5) --------------- -goal: revise VAST paper sections (done 4/6) - integrate VAST with modified networking layer (by Marvin) - - -2009/04/02 (4) --------------- -goal: modify VAST interface to support SPS - integrate VAST with modified networking layer (by Marvin) - -- modify the new VAST interface in VAST.h - -coding roadmap: - -- modify interface (include network layer) -- integrate network layer - -- three main components - - logical client's VON (join, move, leave, list) VON - - publish support pub/sub - - relay join / modify / leave topology-aware - -- simulation evaluation (we need to simulate a physical network?) - - nodes join / leave (churn) - - movements (sub area moving) - - publish (text chat?) - * should see that subscriber list is up to date, publications are sent correctly - - - -2009/03/11 (3) --------------- -goal: make cleaned up VAST version compilable - -- done. however, still can't run properly due to some bugs - - -2009/03/04 (3) --------------- -goal: re-check / refine interface defintion for VAST.h & Network.h - estimate schedule and workload - write down / revise major procedures - - - - -2009/03/03 (2) --------------- -- restart log for VAST (SPS version) - -goal: re-check / refine interface defintion for VAST.h & Network.h - estimate schedule and workload - - - - -2008/11/22 (6) ----------- -goal: remove IP-lookup from Network. Compile existing functionalities - - -2008/11/21 (5) ----------- -goal: remove IP-lookup in Network interface. Make it compilable with existing - functions - -- see that we need to extract common functionality from net_emu & net_ace - such as send/recv messages - -2008/11/20 (4) ----------- -goal: remove complicated stuff from network interface (simplify!) - -- simplify Network.h interface, compile and fix places where the code breaks - -2008/11/19 (3) ----------- -goal: remove complicated stuff from network interface (simplify!) - - - -2008/10/15 (3) --------------- -goal: clean up networking layer - fix memory leak bug (VAST consumes continously increasing memory as simulation proceeds) - - -2008/10/14 (2) --------------- -goal: clean up networking layer - fix memory leak bug (VAST consumes continously increasing memory as simulation proceeds) - -- removing unused codes - -2008/08/27 (3) --------------- -goal: make better consistency - provide same functionality with peer subscription (manager need not move) - -DEBUG (consistency improvement) -- consistency improves dramatically after the following fixes - * no teleport check (do a JOIN only the first time) - * clear up _new_neighbors every time after it's used - * do not clear entries in _new_neighbors simply due to delay (during bootstrapping some NODE info are late) - * add back 'time' to Node structure. Also timestamp node info when they're sent out by MOVE messages - -DEBUG -- consistency was bad if IP address mapping lookup is used. (better consistency is achieved by including Address within Node structure) - found out it's because when doing insertNode, if no address are provided, a connection should still be attempted. - In other words, regardless of whether IP address is provided, insertNode () should always try to establish connection. - - - - -2008/08/26 (2) --------------- -goal: debug VAST so VASTsim_gui is runnable - provide same functionality with peer subscription (manager need not move) - - -DEBUG -- found out when deleting an item in a vector or map, *always* record the item index to be deleted in a list first, - then delete in order. DO NOT delete while going through (iterating) the list. - -DEBUG -- if program crash when deleting something, it's possible it's an exported class that gets NEW outside of its DLL, then attempts - are tried to delete it outside of DLL. Such can happen if the exported class doesn't have STL variables such as map or vector - (because they're NEWed inside the DLL when used). Solution: *always* use a factory class for creation / deletion of DLL objects - this is new/delete are always kept inside the DLL - - -- Modify JOIN mechanism. Now only if a 1st time join will a manager contact the gateway. - -DEBUG -- put back 'time' into Node (seems like Node position updates need 'time' in order to be correct) - - -BUG: -- seems like already known nodes to each other would not update each other about its own position correctly (but others are updated). - not sure why.. - - -DEBUG: -- when doing checks for ENs, one should also check if an enclosing neighbor is a "relevant" neighbor (not just AOI neighbor) to - the moving / joining node. (this way we would not miss non-AOI, but enclosing neighbor discovery) - - -DEBUG An important design decision is when nodes connect to new neighbors, how should the remote address be known? -(big) original VON attaches address info along with Node structure, so every neighbor notification tells the network - address. In a later attempt to try to hide as much Address from the overlay as possible, the upper layer only - notifies "which nodes might know the address of which other nodes", and thus the network layer can - query on its own. This somehow can work.. provided such information about "address knowledge" are kept correctly. - - fix the "neighbors do not update each other" bug above, after adding bogus address during insertNode. - reason is that HELLO messages are not able to reach newly discovered neighbors, due to the lack of address - knowledge. - - - - -2008/08/25 (1) --------------- -goal: VON functionaltiy - Manager can join overlay & move (maintain proper AOI neighbors) - implement: join (), leave (), manage (), subscribe (), move (), getManagers () in new VAST - - -- NOTE: need to make sure when disconnect () in MessageHandler is called, the same physical link is not disconnected until - no handler is interested in the physical link any more (because it's possible more than one handler could utilize - the same physical link) - -- the above is done at 16:20. remove the _net pointer in MessageHandler and allows only MessageQueue has access to network interface. - all connect/disconnect are now handled by MessageQueue where it can keep reference count for each handler that connects to a remote node - only reference count reaches 0 will actual physical disconnection occurs - - - -DEBUG -- got a really strange bug that after new VAST is buildable, building VASTsim (exactly the same code) produces lots of syntax errors. - After spending 40 min. found out it was because there's a new DEFINE in VAST called AOI_BUFFER that replaces the AOI_BUFFER parameter - used in VAST.h - -- finished integrating old VAST code into new VAST, but running GUI still requires debugging. - - - -2008/08/24 (7) --------------- -goal: implement half of the functions in VAST (new Peer and Manager class) - Manager can join overlay & move (maintain proper AOI neighbors) - -- made MessageHandler & MessageQueue work with virtualized handlers (each node can have several handlers serving manager or peer roles) - -TODO: -- found a fairly serious design flaw / limitation, where the unique id for each node is assumed to coincide with the network layer's node ID. - This has the convenience of being able to use NodeID directly to send / recv messages. However, it creates problems when we want to - support multiple handlers to be used with a network address (IP/port pair). As unique handlerID may consist of more than just nodeID or - networkID. - -- convert existing VAST into new generic overlay VAST.. compilable but VASTsim cannot compile. - - - -2008/08/23 (6) --------------- -goal: make new VAST interface compilable with modified VASTsim components - implement half of the functions in VAST (new Peer and Manager class) - - -- copy from OGRE the following, to mask the warning about MessageQueue's use of as part of -its private variables - -- add the following -// disable: " needs to have dll-interface to be used by clients' -// Happens on STL member variables which are not public therefore is ok -# pragma warning (disable : 4251) - -- made VASTsim compilable under new VAST functions. - -- add Peer.h and Manager.h class, inherting MessageHandler. Thought of modifying id to accomodate virtual node roles. - But cannot find a uniform way to put different handlers of messages (VAST, Peer, Manager, VSM... ) using a - unified framework. - - - -2008/08/22 (5) --------------- -goal: make new VAST interface compilable with modified VASTsim components - implement two functions in new VAST - - -- new VAST buildable :) -- introduce new classes - - MessageHandler - can be inherented by any component pluggalbe into the message queue - MessageQueue - represents a unique IP/port interface to network - -- various fixes to make the new library buildable - - - -2008/08/21 (4) --------------- -goal: make new VAST interface compilable with modified VASTsim components - implement one function in new VAST - -- need a proper messaging mechanism / network abstraction that allows messages be sent & received over the network, - but also usable by different components. i.e., a shared hub for network messages - - - -2008/08/20 (3) --------------- -- can compile old VAST under VC9 - -DEBUG -- a linker error where .lib for VAST or VASTsim is not found. Solved first by copying old project file. - later found out it's because the WIN32;_WINDOWS macros are not defined - - -2008/08/19 (2) --------------- -- starts work on generic overlay VAST -- create new project under VC9 for VAST, compilable, also include VASTutil inside VAST, - -BUG: -- VASTsim would be unable to link to some vastverse or VASTutil functions - - - -============================= - - - -2008/03/26 ----------- - -- first try out SVN to get current development from cscsx -- refactor insert_node () so there's only one version (instead of overloaded ones) - - - -2007/12/24 (1) --------------- -goal: merge movement behavior generator for both FLoDsim & VASTsim - -VASTsim now workable version with new VASTutil behavior & file logger - -DEBUG -- get rid of a crash bug caused by the 'delete' of a map within MovementGenerator - by moving the new and delete into .cpp instead of having them in the .h - (something to do with memory allocation / de-allocation cannot occur accross DLL) - -DEBUG -- found out VASTsim has very poor consistency in 10,000x10,000 world with 500 nodes. - discovered reason is that join is not yet finished for all nodes before simulation - starts. Fix by requiring all nodes pass the is_joined () test before starting - simulations - - -2007/12/22 (6) --------------- -goal: merge movement behavior generator for both FLoDsim & VASTsim - - - - -2007/05/01 (1) --------------- - -major DEBUG: -[linux] when demo_gateway is run on Linux, it cannot link back to a U.S. node/host. -- a second cause has been found that in net_ace's disconnect, the mutex for connection object - should not be used - -(potential) BUG: -- VAST's send/recv msg now does not use 'recvtime' but the time received is actually the sender side's - - - -2007/04/30 (1) --------------- - -major DEBUG: -a strange bug while using FLoD that the server is usable under Win32 but not Linux. -- found out it's due to inconsistant byte alignment of the VAST structures between Win32 & Linux. - So that even the first QUERY message cannot be properly processed. - - - -2007/04/19 (4) --------------- -- goal: release VAST 0.3.2 - -RELEASE - - -DEBUG: -DD & RS print-out are out of range and very high. Found out later it's because yuli has recorded -and printed out deflated-send/recv and didn't print the captions, yet the deflated-send/recv sizes -do not record correctly if message compression is not used. temporaily disabled printing if -not using the VAST_MC model. - - - - - -2007/04/11 (3) --------------- - -additions: -- when sending DISCONNECT should use timestamp = 0 in net_emu -- when doing sendmsg () or flush () in net, should return actual # of bytes sent instead of supposed bytes - - - -2007/03/31 (6) --------------- -- ACE 5.5 built successful on linux, after failing manual build, downloaded pre-built - RPM at http://dist.bonsai.com/ken/ace_tao_rpm/ - - then build vast-c++-0.3.1 successful with ACE - - -2007/03/22 (4) --------------- - -MAJOR CHANGE -- add 'vastbuf' class and used for all send/recv buffer within VAST (so that message of - any size can be sent without buffer overflow). However, this does not apply to - UDP messages - - -2007/03/15 (4) --------------- -- hide VASTID from overly exposing (so that other libraries won't have to link to VAST - all the time) - -DEBUG: - -found out chatva client not responding is due to: -1. when gateway replies ID it's not using reliable delivery -2. chatva does not run tick () when ID is not yet obtained, therefore it never processes - incoming messages - -* chatva runs succesfully to up to about 40 nodes (starts to fail joining) in Released mode - but CPU utiliziation is around 80% and seems like the problem might be caused by the - local host not being able to handle it... - - - - -2007/03/10 (6) --------------- -- goal: write visual diagnostic for voronoi construction (large data-set verification) - -DEBUG: -found out topology consistency decreases dramatically for VASTsim in the new model. -discover the cause as logical time not advancing (vastverse is not tick'ed) and nodes -do not process messages as nodes join (so too many join request may accumulate). Solve -this by adding back the node processing. however this effectively decrease the -data collection. - - - -2007/02/09 (5) --------------- -- goal: finish adding new materials to NOSSDAV draft - -paper additions: - -- add considerations for -- smooth out paper - - - - - -2007/01/30 (2) --------------- -- goal: results, convert to Latex - -DEBUG: peers have very large IDs - cause: VASTATE is not ticked when peer joins in the beginning.. causing many IDs to be - assigned to the same peer - -VASTATE -BUG -- peer receives message deleting its own avatar object -- arbitrator thinks it is owner, then delete a peer object, notifies others (so no one now has it) - yet the peer still wishes to connect... - -- notices that after a long pause at one step, many pos_version = 0, then crashes... - - -what would be some of my own questions? for VSP..? - -failure - - what happens when node fail ? arbitrator fail.. can you recover? under what condition? - how much cost? how effective is it? - -load balance - - will nodes really distribute load? how much cost? how effective is it? - - load of the server/arbitrator, initially? later? - -scalability -- can your system scale? -- what are some bottleneck points? in what situation.. - -bootstrapping - - how is server inserted? - - how much arbitrator capacity is required? - -others - - can an event / object becomes invalid? - - how does node capability percentage/distribution affect loading/load balancing? - - -Overload/Underload threshold -27, 66, 191kb for min, avg, max during randomwalk test (600x600, with 100, 200 & 400 people) - 11, 22, 44 / region (out of 9) - 46.5 - - 50 & 130kb - underload overload - - - -2007/01/29 (1) --------------- -- goal: finish whole draft -- done: procedures, ver 1-0. - -VASTATE bug: -- ownership received but object doesn't exist.. - -FUTURE WORK: -- for boundary arbitrators, they could backup to nodes that'll never be able to take-over - (thus wasting storage...) - - - - -2007/01/28 (7) --------------- -- goal: finish draft (procedure - morning, evaluation - afternoon, intro & background - evening) -- done: psuedo code - -VASTATE bug note: - -- a peer sends three movements + one grab event of food to arbitrator (all delayed to be executed - in the same run), event may not be forwarded to the enclosing neighbor as forwarding - was based on current location. if the 'food' is in another region, the region owner may not - have received & executed the event - -- when arbitrator updates its list of enclosing arbitrators (EAs) and find that a node is no longer the - EA, it would send DELETE to that EA for objects it owns. This currently works as VAST does not - disconnect right away non-EA nodes, and the EA judgment is done internally on a separate Voronoi - by the arbitrator. However, in real situations, it's possible the EA link is already destroyed by - VAST, making DELETE unable to reach the EA, causing zombie objects. - - - - -2007/01/26 (5) --------------- - -- VASTATE: events that create object may be processed by all enclosing arbitrators - -> multiple ownerships - - - - peer if join fail, then need to re-join via Gateway - - send 'tick' for objects, record last updated event, remove aged zombie objects - - rockets should create its own event by arbitrator - - -new arbitrator notification should send along pending events - - - -2007/01/17 (3) --------------- -- goal: finish VASTATE implementation - -- LEAVE handled for both peers & arbitrators (fault-tolerance) -- arbitrator promotion/demotion requests mechanism changed (to querying the VASTATE interface) -- AOI adjustment for arbitrators - - -2007/01/16 (2) --------------- -- goal: finish VASTATE implementation - - -- enable new ID scheme in VASTATE (compilable) -- add cleanup mechanism to net_emu in VAST - (when stop() is called, all connections are dropped/disconnected, - also, message queue is freed up in destructor) - - -2007/01/15 (1) --------------- -- goal: finish VASTATE implementation - -- "chatva" now workable with the new VAST (separated ID assignment) - -- VASTATE modified to suit the new VAST (compilable), but initial ID assignment not yet integrated - -DEBUG -- add critical sections to various access to the network connections in net_ace to avoid thread - contentions of the connection (sending while deleting) - - - -2007/01/10 (3) --------------- -- goal: add LEAVE procedure - modify network.h to add message handler hooks & subsequent user behaviors (VAST, VASTATE, FLoD) - - - - -2007/01/09 (2) --------------- -- goal: debug the remove non-AOI objects for arbitrator - -DEBUG -- discover that the bug was caused by non-AOI objects' positions are no longer sent to the - arbitrators that cannot see them, the positions were there still out-dated and will not be - removed by the remote arbitrators' removal mechanism. Fix the problem by having the sending - arbitrator makes all the decisions (including whether to remove non-visible AOI-objects, by - sending a OBJECT update with pos_version = 0). - - - -2007/01/03 (3) --------------- -- goal: implement LEAVE mechanism in VASTATE - - - -2007/01/02 (2) --------------- - -DEBUG -- discover a bug in net_ace_handler attempt to delete a UDP pointer to an - ACE_SOCK_Dgram object, which was actually supplied by net_ace. So modify the UDP socket creation - process and create the openUDP () for net_ace_handler. Tested with 3D streaming app - where program exit no longer crashes - -- another crash bug when terminating, is when deleting a vast node under VASTsim. - cause is that the 'network' interface for some nodes are already removed, yet other nodes would - still attempt to use them in order to call remote_disconnect (). Simple fix by unregistring - the network object from bridge when deleting. - -- increase buffer size from 4096 to 40960 as if a TCP message exceeds 4096 the program will simply - crash - - -2006/10/03 (2) --------------- -- goal: add 'storage' interface to VASTATE (allow query for stored data item without necessarily using C/S or distributed - query/delivery). - - - - -2006/09/27 (3) --------------- -- goal: debug VASTATE (remove non-AOI crash bug & consistency decrease) - -- remove non-AOI objects for arbitrators now no longer crashes (as now all objects are created within - an arbitrator's AOI. however, removal would decrease the update consistency for states. Also, - discovery consistency is not 100% some of the times, reasons still unknown. - - - - - -2006/09/26 (2) --------------- -- goal: debug VASTATE (remove non-AOI crash bug & consistency decrease) - - -BUG (big): - 1) in VAST, a boundary neighbor may not properly notify for new neighbor discovery, if it considers - the moving node and the new neighbor as enclosing neighbors of each other, when in fact they - aren't (this is due to the boundary neighbor not knowing another more distant neighbor that - prevents the moving node and the new neighbor to be mutually enclosing.) - - soluions? (connect beyond EN set when connection limit is not yet reached? in other words, - dynamically increase AOI-radius when CN is not reached, and adjust back to maintain fixed - number of connected neighbors?) - - -2006/09/20 (3) --------------- -- goal: debug VASTATE, add arbitrator object management (removing non-AOI objects periodically) - - -- add 'beh_clustered' to the stock of behavior_models - - - -2006/09/19 (2) --------------- -- goal: debug VASTATE - -DEBUG: - 1) found a bug where the arbitrator notifies a peer of new objects (using extended AOI-radius) - but the peer immediately removes it by using only its original radius to check - (object undiscovery therefore occurs) - - - solve this problem by dividing the client object discovery process into two parts: - storage & notification. where a notification occurs only if the object comes into AOI view. - but will only be remove from object store if it leaves an buffered AOI. update consistency - has come to 100% after 3 steps in most cases. - -- convert project file to .net2003 (all projects compilable) - - -2006/09/13 (3) --------------- -- goal: debug VASTATE - -DEBUG: - 1) an annoying bug where many times, the program would crash within sfvoronoi, where doing - a free (buf_list[]) in calvs() tries to free invalid buf_list. - - the bug (found by �ֺd) was that in processmsg of VAST, we'd collect stat info of msgtype - via - - _msg_stat[msgtype] += size; - - But after VASTATE is added (and it uses msg number starting from 100), it access - unallocated memory in _msg_stat, causing data in Voronoi to corrupt. - - A fairly easy fix, but difficult-to-trace problem. - - after this is solved, not only voronoi works properly now, a previous "unable to run under - Release mode" problem is also solved. - - -_msg_stat[msgtype] += size; - -2006/09/09 (6) --------------- -- goal: debug VASTATE - -DEBUG: - 1) after updating interests using not just owned objects, but all known objects - (but do not use arbitrators as destinations), the program would mysteriously crash - upon init. Trace reveals that the map '_interested' contains NULL items - (that is, a 'find' would get something, but the value is 0, or NULL). - suspects that it's because of an access such as - - notify_peers (_interested[info.obj_id], STATE, msg, size); - - although no value is assigned to _interested, by accessing it using [] operator, - a NULL value is put into the map - - * confirmed the hypothesis and solved the bug at 18:00 - -BUG: - 1) small bug, if a node doesn't move, it won't be made known to a peer joined later - (even if it's in the AOI of the late peer). Initial object notification wasn't done properly? - - -2006/09/08 (5) --------------- -- goal: debug VASTATE - -BUG: - 1) neighbor undiscovery across regions (peers cannot learn of AOI neighbors in another region) - 2) removal of non-AOI objects - -MAJOR CHANGE: - revise directory structure, now into the following: - /bin - binaries - /demo - demo files (chatva, gateway) - /errout - error_out - /include - shared include (VAST, VASTATE, VASTsim, VASTATEsim) - /lib - libraries (unused) - /sim - simulations - /test - test files - /VAST - VAST files - /VASTATE - VASTATE files - -Projects have also changed names into - errout - VAST - VASTATE - VASTATEsim - VASTATEsim_gui - VASTsim - VASTsim_console - VASTsim_gui - - - -2006/09/06 (3) --------------- -- goal: debug VASTATE - -DEBUG: - Another bug found where the peer does not remove non-AOI objects is that: the arbitrators immediately - stops sending updated position to a peer if the objects become non-AOI, however, the peer thus - still retains old knowledge and considers the objects to be AOI-objects. So the peer does not - remove the non-AOI object. - - - -2006/09/05 (2) --------------- -- goal: debug VASTATE - -DEBUG: -'_peers' was not updated after initial creation, solves the bug where some nodes stop moving after a while.. -(because they were seen as 'not interested' in the object updates, due to their positions were - never updated after initialization) - - -2006/09/01 (5) --------------- -- goal: debug VASTATE - -BUG: - 1. system consistency in 4 steps would drop after simulation has run for a while - 2. object attribute version would still increase even if they were not modified. - - - - -2006/08/17 (4) --------------- -- goal: debug the peer not moving issue - -DEBUG - solved the issue of peers not moving when crossing arbitrator regions. - found the issue were - 1) the peer did not learn properly the arbitrator list when entering a new region - 2) region transfer check was not called by peer - - - -2006/08/12 (6) --------------- -- goal: finish first implementation of VASTATE (load - balancing) - -- finished first completed implementation of VASTATE - - -2006/08/11 (5) --------------- -- goal: finish first implementation of VASTATE (load - balancing) - - - -2006/08/09 (3) --------------- -- goal: add n-tier neighbor to Voronoi class - finish first implementation of VASTATE (load - balancing) - -- add a 'level' parameter to the Voronoi class's get_en (), so that which levels of ENs may be - specified. - -MAJOR CHANGE: -- change the internal data representation of a "Position" class from 'long' to 'double' - - - -2006/07/28 (5) --------------- -- goal: finish first implementation of VASTATE (load - balancing) - -DEBUG -- solve the event ordering issue by revising net_emu, where messages are now handled by -a multimap with timestep as index, as opposed to the linklist. - -DEBUG - be careful of using the "[] = NULL" to check for something that's not in the map, - it could very well be that something exists but returns NULL (0) nevertheless.. - -succesfull make event forwarding to work, however, found out that if two foreign nodes -both try to use their previous values to update a new value, then the effect of -one operation would be cancelled. For example, if node 1 owns A, with value 7. - -Then in one step node 2, 3 all try to increment.. with the knowledge that A = 7. -Then both would send in the request of A = 8 to node 1, where after applying both -updates, the result will be A = 8. However, it might sometimes be desirable the result -is A = 9 (applying two +1 operations). - -But at least updates are displayed and notifed consistently across nodes. - - - -2006/07/27 (4) --------------- -- goal: finish first implementation of VASTATE (ownership check/transfer & load-balacning mechanism) - -finish 1st working ver of owner transfer check -still has small problem about event ordering (inserting a timestep=0 event would not come -in the beginning of message queue) - - - -2006/07/26 (3) --------------- -- goal: finish first implementation of VASTATE (ownership check/transfer & load-balacning mechanism) - -The process: - - send in command (an update to a particular/specified object) - - generate bytestring - - deliver to event handler (local or remote) - - execute the request after checking ownership (owned: execute, un-owned: forward to owner) - - - - -2006/07/25 (2) --------------- -- goal: finish first implementation of VASTATE - -DEBUG - solved an annoying bug where the encode/decode of messages seem to have problems - (decoding incorrect bytestring). found out after much inspection that the problem - was in for loop, the 3rd paremter will actually execute/validate first before seeing - if the 2nd parameter will run.. and I had an pointer advancing like this - - for (int i=0; i 150 steps) some nodes would keep large number of - neighbors (25 in a 30 node simulation, for example). When trying - to trace in Debug mode, it would crash after steps 23 - 25. - However, if using releae mode then it wouldn't crash. - - -2005/08/28 (7) --------------- -- goal: improve dAOI consistency - - -2005/08/27 (6) --------------- -- goal: improve consistency while saving bandwidth - - -UNDISCOVERY (simucase-10) - a new node may not be discovered in time because eligible BNs that are - able to notify are also just learned by the moving node. - - solution: increase the detection radius (BUFFER_MULTIPLIER increased to 3) - correction: it's not necessary to increase BUFFER_MULTIPLIER, - all we need to do is to +1 in on the AOI-radius while - doing neighbor discovery check (!) - -Ver 0.2.8 (collect stat after stablized) -------------- -DEBUG fix a small initialization bug in GUI by adding a bit displacement - when doing initial join (helps to ensure system will stablize) - (there's a position set that would make GUI pause for a long time - waiting for stabliziation) - -BUG found out for the same set of data, the crashset would produce - inconsistency at a very late step (reason unknown) while - if the fullset is run (1000 steps) then no inconsistency would - occur - -DEBUG found out that if the simulation runs without first doing - displacment-based stablization, then consistency would be good. - (weird thing described in previous BUG doesn't happen) - decided to not run psuedo-stablization steps but run - simulation directly. only to reset stat counters after - reaching first 100% consistency. - - -2005/08/26 (5) --------------- -- goal: improve consistency while saving bandwidth - -- initial runs of v0.2.7 shows that consistency has improved, however, - inconsistency still occurs for nodes above 400 - (400 - 99.9994%) - (500 - 99.9693%) - (600 - 100.0000%) - - it was quite frustrating until I check out where does the inconsistency - occurs -- at the beginning of the simulations at time-step 11 - (beyond step 10, the data collection point) - - solution: try to calculate/collect stat only after the system has - stablized to the first 100% point. also collect # of time-steps to - reach stable state. (done!) - -- another observation is that bandwidth use under v0.2.7 increases - exponentially. % of NODE message repeats confirms this, where it rises - fairly high between 70% - 90% of all NODE messages as # of nodes increases. - -- print inconsistent nodes within logfile - - -2005/08/25 (4) --------------- -- goal: improve consistency while saving bandwidth - -- some stats: - - check all: all known neighbors are checked for new overlap or new EN - check EN: only EN are used for neighbor discovery check - no clear: does not clear out _neighbor_states after MOVE is received - -Simulation Parameters -nodes: 50 world: (600, 600) steps: 1000 -aoi: 100 connlimit: 0 velocity: 5 lossrate: 0 failrate: 0 - - Neighbor discovery methods - -------------------------- - check all check EN check all check EN - (no clear) (no clear) ----------------------------------------------------------------- -max_drift 15 10 10 10 -consistency 100.0000% 99.9996% 100.0000% 99.9996% -avg. trans 2671 2398 2436 2278 -max. trans 11431 8749 8874 8023 -NODE repeat % 0.749 0.775 0.664 0.726 -NODE repeat # 2643 1939 1714 1468 - - -Ver 0.2.6 (batch processing of NODE messages) -------------- -DEBUG found out when processing NODE message, a node may be judged if - it's relevant on a per node (message) basis. However, this could - be slightly incorrect if not all new node positions are considered. - therefore we should make the judgement only after updating the - most recent/correct position of all moving neighbors. - - fix this problem by recording all NODE messages first and then - do connection decision in a batch. consistency has improved slightly. - -Ver 0.2.7 (time check) -------------- -DEBUG improve drift distance (and hopefully consistency as well) - by ensuring each NODE message contains a timestamp, and only - the more recent timestamped message would be used during - notification. - - add parameter 'time' into Node data struct in typedef.h - also changes aoi_t from 'long' to 'short' (so that a Node - structure remains to be 16 bytes) - -2005/08/24 (3) --------------- -- goal: improve consistency while saving bandwidth - -- some testing shows that about 75% of NODE messages were redundent - (notifying for nodes already known). And more redundent messages exist - when all neighbors are being checked than just ENs (this explains the - surge of bandwidth use). Certainly an area for improvement. - - some ideas are: - - when a node beings to send MOVE instead of MOVE_B, this clears the - neighbor_states list, however this will cause much redundent NODE - messages be sent when the moving node again sends out MOVE_B - so overlap & EN checks should always be performed (but sends NODE - only when necessary) - - - try to see if there are deterministic methods to separate NODE - message streams (that is, among the notifying nodes, try not to - send redundent NODE messages) - - -2005/08/18 (4) --------------- -- goal: write 1. related works 2. DT-overlay - -DEBUG the crash bug from the new fail/join mechanism is caused - by accessing _neighbor_states during the neighbor discovery - stage. _neighbor_states becomes invalid because right now - neighbor discovery is done in batch, so there could be - DISCONNECT messages in between MOVE_B. Add a check to prevent - invalid access of _neighbor_states. - -- add a small bandwidth saver: - store the info sent by EN message as the initial data in - _neighbor_states (assumed that the sender considered those nodes in EN as - overlapped). this saves about 5% bandwidth use. - -- found out that when doing neighbor discovery check, using only the - EN and all neighbors for the check have different consequence: - using EN only saves bandwidth but has lower consistency, while using - all neighbors usually give very good consistency but uses at least 10% - more bandwidth - -2005/08/17 (3) --------------- -- goal: implement node fail/join mechanism - -- did a version that implements join/fail, by specifying a FAIL_RATE, - then within VASTsim library's NextStep () it would randomly make a - node leave the overlay, then re-join with a new position. Also modify - vast_dc accordingly to prevent re-obtaining node id. compiled alright - but would crash when run. - -2005/08/16 (2) --------------- -- goal: achieve 100% consistency for basic & dAOI model - -DEBUG -- investigate the seemingly large drift distance. found out part of the - problem is that when a node is first learnt, its position might not be - the most up-to-date, therefore it would have some non-zero drift. - however, the situation is usually resolved after the node has moved - into the AOI. Therefore, calcuation of drift distance should be - modified to only that within AOI. - -Thoughts on VAST 1.0 --------------------- - -Two new developers will be joining the developments of VAST: -Chang Chen Ye-Zen and Yakko. Both were friends I met back while at -the Computing Center of NSYSU, and have been professional software -developers. - -There were some discussions over the weekend on what we should do next, -and Yakko suggested to make a To-Do list for perhaps VAST version 1.0 -- -a practically useable version. So here are some initial thoughts organized -into three categories: -(you're welcome to contribute ideas to add or modify the list) - - -Research --------- -- Latency tolerance mechanism (consistency under packet delay & loss) - - packet delay and loss are inevitable on real networks, therefore - techniques are needed to deal with latency, jitter, and packet loss. - 100% consistency in "logical time" can be maintained if we can wait - indefinitely for retransmission of late or lost packets. however, - "real-time" requirement usually does not allow this. - - -Design ------- -- Data format and protocol specification. - - VAST might be adopted more widely if a set of common protocol and data - formats are developed. - - -Implementation --------------- -- 100% topology consistency and low drift under 0% packet loss, - and "good enough" performance (for example, above 95% consistency) when - it is acceptable for some packets to be lost. - - The first scenario is useful when real-time response is not strictly required - (for example, when we're running a simulation without real people involved, - but accuracy of node positions is required), - and the second is usually the case for applications that - involve real human avatars (when we cannot wait for delayed packets indefinitely) - - This characteristic will need to be tested by simulation. - -- NAT-transversal. - VAST should be usable for people who're behind VPNs. - -- Cross-platform network transmission. - delivery of reliable and best-effort data should be supported. - (that is, regular and reliable UDP delivery) - - -2005/08/15 (1) --------------- -- goal: achieve 100% consistency for basic model - -- found out the addition of sending DISCONNECT message would cost - much bandwidth (20% more bandwidth to obtain slight increase in - consistency). Experiment with an alternative approach: - do not send DISCONNECT message, but notify the moving node if - a node becomes overlapping new neighbor, regardless of whether it was - previously notified as an EN. This will preserve much of - the original mechanism, and there will be no DISCONNECT message - to send. this approach increases traffic by about 5% than the OVERLAP_MULTIPLIER=4 - approach for 50 nodes in 600x600 environment. - - following is a list of comparison of various approaches: - (simulation parameter: 600x600 50 nodes, 1000 steps) - - Approach Consistency Avg. Trans. Max Trans. (bytes/second) - ---------------------------------------------------------------------- - original 99.9996% 2700 8858 - (overlap multiplier=4) - ---------------------------------------------------------------------- - DISCONNECT 100.0000% 3269 11444 - (no NODE_EN) - ---------------------------------------------------------------------- - DISCONNECT 100.0000% 3281 11520 - (NODE_EN) - ---------------------------------------------------------------------- - no DISCONNECT 100.0000% 2878 10512 - (separately notify) - - -2005/08/14 (7) --------------- -- goal: try to solve the less than 100% consistency problem with - OVERLAP_MULTIPLIER set to 2 (instead of 4) - -DEBUG achieved 100% consistency in test run after adding - DISCONNECT message to include when a node refuses to connect - to a NODE message (notify the BN that the NODE message was - not processed, so that it may has a more correct view of - "known neighbors" for the mover) - - -- fix a small bug to exclude calcuation of internally generated - DISCONNECT message (this causes higher amount of RECV traffics than - actual, avg SEND and RECV should be the same amount when it's 0 packet loss) - -- - - -2005/08/12 (5) --------------- -- goal: try to solve the less than 100% consistency problem with - OVERLAP_MULTIPLIER set to 2 (instead of 4) - -- found out when to call remove_nonoverlapped () is a tricky decision. - currently it is done immediately when setpos () is called. - however, there are two other places where it might be placed: - 1. at the end of setpos () - 2. at the end of processmsg () - - however, the other two possibilities generate lower consistency than - when it's called right when setpos is called (reason unknown) - - -BUG found out a troubling issue related to the algorithm itself: -(big) nodes may have different judgements about enclosing neighbors, - which causes missing neighbor discovery. for example: - - it is possible that node A may disconnect node B because - node B fails both the overlap and enclosing neighbor test. - however, a middle node C may still consider node B to be node A's EN - (due to the fact that node C and node A have different known nodes) - therefore, to node C, node A should know node B (that it is unaware - of the disconnection between A & B) - - two possible solutions: - 1. node A also notifies node C when it disconnects node B - 2. node C does not keep enclosing neighbors as part of the - "known-node" list, thus would continously notify for new EN, - and would also notify when two nodes become overlapped. - - after some testing, solution 2 does increase consistency, - at the cost of more traffics (an increase from 200% to 10%, - depending on node density) - -BUG after applying solution #2 above, there's still some inconsistency. - Tracing shows that it's because due to the way behavior model - generates positions, a node may record its own maximum speed - as somewhat less than actual (for example, 4 instead of 5). - This would cause misjudgement when this node is doing - neighbor discovery for others. If it uses other nodes's speed - as max_velocity, there'd also be problem if a movement packet - is skipped (due to unknown reason, possibily a reconnect followed - by disconnection?) and causing max_velocity to be - greater than actual. - - solution: specify max_velocity when creating a vast node - instead of calculating from position records - (not adopted, still use recorded results, in order to maintain - the separation of functionality) - -NOTE there's another situation that needs to be considered, which is - when Node1 notifies Node2 for a new neighbor Node3, it would - subsequently assume that Node 2 will be aware of Node3. However, - in reality, Node2 would judge independently whether to make the - connection, therefore Node1's knowledge could be wrong and - does not notify again when it should. However, analyzing further, - this shouldn't happen for overlap test (as it is universal) - but only would happen for enclosing neighbor test, so perhaps - solution 2 above is a better choice, though it might generate - more traffics - -NOTE: an important observation is that, for neighbor discovery - notification to properly function, it's quite important that the - "buffer area" is correct. Buffer area is ideally the combined - distance two potentially visible nodes travel to each other. - However, if the buffer area is specified, we will need assumption - about maximum velocity of all nodes. If it's too large then - connections could be wasted, if it's too small then undiscovery - could occur. - -BUG another subtle bug: because during neighbor discovery check, only - ENs are involved. therefore if a new neighbor - that should be learned is not the EN of the checking node, then - neighbor discovery could miss until other mechanisms set in - (for example, both A & B ask C to check for new neighbor, - A is C's EN but not B, therefore B would be notified of A but - A isn't notified of B. A will learn of B when B contacts A, - or when B becomes some other nodes' EN which A requests new - neighbor check) - - solution: one simple solution is to check for new - neighbor discovery from *ALL* known nodes instead of just the EN set - (adopted) - - -2005/08/11 (4) --------------- -- goal: try to solve the less than 100% consistency problem with - OVERLAP_MULTIPLIER set to 2 (instead of 4) - -- organize trace messages - -DEBUG: found out overlap check would miss because _max_velocity was 4 - (instead of the correct 5) as it was actually re-calculated - every time-step. the problem seems to disappear (for node=30) - after making sure _max_velocity was indeed 5 - - -2005/07/05 (2) --------------- -- goal: put first initial release of VAST (v0.1) - (but must be relatively stable under emulator mode and - can generate all results in the paper without problems) - add packet_loss parameter - - -2005/07/04 (1) --------------- -- did some testing & experiments, found out - - using a OVERLAP_RADIUS_MULTIPLIER of 4 can almost guarantee 100% consistency - even with velocity of 10, though 3 already works well for velocity of 5 - - however, if NODE messages are processed in the same time-step as when the - neighbor discovery is made (instead of the next step), using a multiplier of - 2 is enough (vel=10). - - This supports the hypothesis that the multiplier basically - serve as a counter-measure to latency. - - - -2005/07/02 (6) --------------- -- attempt to increase consistency while decrasing gap between - CN & AN. - -- tried to fall back to original overlap check method and see if - consistency can be maintained without using OVERLAP_RADIUS_MULTIPLIER - - had first series of simulation results.. CN increases while consistency - is maintained, though more inconsistent in dAOI model. - -2005/06/16 (4) --------------- -- goal: get new version to work on linux & start generating new result sets - for IEEE Network paper. - -DEBUG on linux the behavior model seems to generate non-random movements - (initial positions for many steps do not change at all) - appearantly every time the program calls the move_all(), - random number are reset according to current time. - - solved by keeping a _last_seed to reseed random number generator - every time. This brings movements to be much more random. - - however, for the same set of positions, linux and windows - generate slightly different results (reason unknown) - - - -2005/06/15 (3) --------------- -- goal: debug dAOI inconsistency problem (20:30) - -Simulation Parameters for below comments - nodes: 30 world: (600, 600) steps: 990 - aoi: 200 connlimit: 13 velocity: 10 lossrate: 0 - -- experiment with various multiplier when doing nonoverlap test. - found out 1x _max_velocity works best (99.88% consistency) - with more multiplier causing decreasing consistency. - however, lowest consistency is experiecned (99.78%) when not - using any multiplier. - -- even better consistency (99.9155%) is achieved if no multiplier is - used to check if the node to be disconnected has larger AOI to cover me - (reduce from 4x _max_velocity to 0 or 1), - however AOI-radius is also reduced dramatically. - -- after some trials, appearant a BUFFER_MULTIPLIER of 4 will produce - 100% consistency in basic model. However, for dAOI model, using - a BUFFER_MULTIPLIER of 4 but when doing remove_nonoverlap() use only - a multplier of 1 seems to achieve better consistency. - likely reason is that in dAOI it's necessary/preferable to have - connections be disconnected as quickly as possible if no longer - relevant (otherwise will take up valuable connection resource) - -DEBUG min_aoi in some cases is 0 or smaller than 0 when running in -(big) dAOI mode. found out it was because aoi_t was set to "unsigned long" - which did not allow for negative numbers. However, when shrinking - AOI it's possible to get negative, which in turn becomes a very - large positive number. therefore the <= 5 test would not reveal problem. - - fixed by changing "unsigned long" to "long" - -MILESTONE - achieves close to 100% consistency for dAOI after accepting all - remote connections and do not disconnect unless both nodes are - mutually unaware, specifically the following modifications are made: - (recorded as simu-cases "undiscovery 6") - - - removes the HELLO_E to accept all incoming HELLO - - send out OVERCAP when trying to disconnect a remote node that - still considers me as its AOI neighbor - - drop the use of _dist_drop (count_drop was enough) - -2005/06/14 (2) --------------- -- goal: debug dAOI inconsistency problem - -DEBUG: a rare undiscovery problem is caused by the remote node's - notification of a new neighbor. Yet the node that was - notified did not think the new node falls within its AOI - (inconsistent judgement as to whether the new node is - *overlapped*) However, as the remote nodes only notify - once they go on thinking the new node has been learnt. - usually this can be avoided as the new node would likely - also be notified of this node. however, in this case - the node node was already over-connected, therefore respond - by shrinking AOI instead of initiating the connection. - - solution: add a buffer of 5 units when doing overlap check - when receiving NODE message. also decrease OVERLAP_RADIUS_MULTIPLIER - from 0.20 to 0.15 - - consistency 99.9036% -> 99.9950% - - parameters: - nodes: 25 world: (600, 600) steps: 990 - aoi: 100 connlimit: 10 velocity: 5 lossrate: 0 - - OVERLAP_RADIUS_MULTIPLIER consistency - 0.05 99.9147% - 0.10 99.9918% - 0.15 99.9950% - 0.20 99.9583% - - NOTE: dAOI consistency fixes in 06/13 and 06/14 are stored as - simu-case "undiscovery-5 (incremental fixes in dAOI)" - -UPDATE major modifications: - - replace MAX_SPEED, OVERLAP_RADIUS_MULTIPLIER, MAX_DROP_DISTANCE - in "vast_dc.h" by calculating max_velocity on the fly and - record remote node's most recent velocity. - - added BUFFER_MULTIPLIER and set to 4 initially. - - also change the return value of dist () from long to double - in "typedef.h" (Point class) - - - -2005/06/13 (1) --------------- -- goal: debug dAOI inconsistency problem - - -DEBUG one dAOI inconsistency problem occurs because in adjust_aoi () - the judgement of whether a new adjusted AOI is smaller than the original - did not take into account of OVERLAP_RADIUS_MULTIPLIER.. - so adjustments weren't made when they should. - - 25 nodes @ 600x600 - consistency 99.8749% -> 99.9036% - -DEBUG another problem was caused by removing non-overlapped neighbors - that has a larger AOI (therefore considers myself as within AOI). - seems like the OVERCAP message was not processed properly. - however, also found that the node that disconnects still has - spare capacity. made a modification to allow continued connection. - - consistency 99.9036% -> 99.9335% - -DEBUG when doing remote_disconnect, a DISCONNECT message is generated - automatically at the disconnected node with timestamp 0, - which gives it higher priority than a potential OVERCAP message - (which notifies the disconnected node to shrink its own AOI) - - consistency 99.9335% -> 99.9616% - - -2005/06/12 (7) --------------- -- goal: debug dAOI inconsistency problem - -DEBUG: found out the last joined node always knows neighbors from one step - later (so position is always inaccurate, which would increase - drift distance by a certain amount) - - fixed by making time_stamp to progress for every call to setpos() - even if the node does not actually move (unique ID not yet received) - consistency actually drops from 99.87% to 99.85% for 25 nodes in - a 600x600 environment. though avg_drift improves from 0.33 to 0.12 - -2005/06/09 (4) --------------- -- goal: debug dAOI inconsistency problem - -DEBUG found one dAOI bug happens at the processing of HELLO message, - because it would send OVERCAP if a node is already exceeded in its - limit. however, if the HELLO message is sent from an already-connected - node, the same check still applies and could disconnect one that's - already-connected (violate valid scenarios for disconnection) - - solution: check if the sender of HELLO is already connected, if so - then simply just update the node information. - - -2005/06/08 (3) --------------- -- goal: fix the process-within-same-timestep problem in the emulator (21:00) - debug dAOI inconsistency problem - -- added a _time variable to vast_dc class, also modify sendmsg(), recvmsg(), - and storemsg() in net_emu.cpp to include passing the current logical - time. Found out the difference after taking logical time into consideration - (so that no node may process message generated by other nodes within the - same time-step) is that consistency drops slightly (for 30 nodes) - but was subsequently fixed by adjusting OVERLAP_RADIUS_MULTIPLIER - from 1.10 to 1.20. Drift distance also increases (from 0.03 to 0.24) - -- 100% also achieved for 100 nodes with the new delay - - -2005/06/07 (2) --------------- -- goal: debug basic model inconsistency problem (11:10pm) - debug dAOI inconsistency problem - -- add 'w' 's' 'a' 'd' 'o' commands for moving the GUI viewport - UP DOWN LEFT RIGHT and back to ORIGIN to the GUI interface - -- achieves 100% for 1000 nodes @ 1000 timesteps - -DEBUG improved consistency in basic model by resetting drop counters - when receiving NODE message for already-connected nodes - -BUG there's a nasty inconsistency problem that seems to result -(big) from Node A learns of Node B in the same step after Node A - disconnects Node B. So within the same step Node A does: - - 1. disconnects Node B (sends Node B a DISCONNECT message) - 2. learns of Node B through NODE again (sends HELLO, so reconnects to Node B) - 3. displays Node B correctly - - however, in the next step - 1. Node B process the disconnect message from Node A - and sends to Node A a DISCONNECT when doing a delete_node - (NOTE: usually Node B cannot send Node A a DISCONNECT as - the connection should already be broken, yet here - it is possible as Node A has re-established the connection again) - 2. Node B process the HELLO message (so learns of Node A again) - 3. Node A process the DISCONNECT from node B (so now Node B doesn't exist) - and sends another DISCONNECT to Node B - - at this point Node A doesn't see Node B, while Node B still sees Node A - in the next step - 1. Node B process the DISCONNECT (so finally Node B also loses Node A) - - and likely because the neighbors of Node A and Node B think - they've already done the notification, so no new notifications - are sent until several steps later. - -DEBUG fix the above bug by preventing sending DISCONNECTION again -(big) if delete_node is called as a response to a DISCONNECT message - (recorded as "undiscovery-4" in /simu-case) - - -2005/06/06 (1) --------------- -- goal: debug dAOI inconsistency problem - -BUG: slight inconsistency might exist due to bootstrapping - in the beginning of simulation. - -IDEA: consistency under direct connection model without AOI - adjustment (the most basic model) is actually important, - in the sense that for application such as protein folding - (MD simulation), it is necessary to have 100% consistent - (fully-synchronized) simulation. This is when the real-time - requirement is droppable. - - -2005/06/04 (6) --------------- -- put on first version of VAST's public website at sourceforge - (http://vast.sourceforge.net) - - -2005/06/02 (4) --------------- -- goal: simulate dAOI model and tune - write VAST description for website at sourceforge - - -2005/06/01 (3) --------------- -- goal: debug inconsistency problem (3:30pm) - 100% consistency @ 500 nodes (20:42) - -BUG: a)found out a rare inconsistent case where node A disconnects node B -(big) (as it's out of its AOI with enough drop count), however, just at - that particular step, node B actually has moved into node A's AOI. - But node A misses this as it hasn't known node B's most recent - position when making the disconnection decision. - b)Coupled with this problem is that the neighbors of node A and B - think they still know each other (could be because a new neighbor - notification was sent earlier, so these neighbors *thought* the - two know each other), so no neighbor notification is sent. - - possible solution: [1] use a "drop distance" instead of "drop count" - when the total distance has accumulated over a threshold then - do we disconnect. [2] A second solution is to do the disconnection - check (i.e. remove_nonoverlap()) after learning all the new positions - of neighbors. - -DEBUG: solve problem b) using solution [2] by calling - remove_nonoverlapped() before actually setting new position - in setpos() - -MILESTONE -DEBUG: achieved 100% consistency for up to 100 nodes (AOI-100) -(big) by adding "drop distance". After testing, use both - "drop count" and "drop distance" when deciding disconnection. - (inconsistncy still occurs by using "drop distance" alone) - -IDEA: after consistency is achieved, observe that OVERLAP_RADIUS_MULTIPLIER - indeed plays a role on consistency in that while both the multiplier - 1.10 and 1.05 achieves 100% consistency for 110 nodes, AOI: 150. - there are cases of inconsistency that recovers within 1 step for - multiplier 1.05. - -BUG: consistency drops considerably starting after 100 nodes, -(big) serious problem at 500 nodes, crash at 1000 nodes - initial overlay partition occurs during one 110 node trial - likely causes: - 1) joining too many nodes initially (should do so incrementally) - 2) buffer size inside Voronoi class too small - 3) initial ID-assignment problem (display incorrect ID after node 128) - -DEBUG solve scalability problem by treating case 3) - by replacing the original id assignment - _self.id = (id_t)*msg; - with - memcpy (&_self.id, (void *)msg, sizeof (id_t)); - - then, treat case 1) by run processmsg() inside CreateNode () - successfully run 500 nodes for 100 steps - -BUG: still has segmentation fault when running 1000 nodes - possible cause: exceed neighbor size maintained per node - (in SFVoronoi.h, set to 100 currently) - -2005/05/31 (2) --------------- -- goal: debug inconsistency problem - -DEBUG found out one undiscovery problem is caused by the checking node - has not processed the MOVE commands of all neighbors, therefore - using an outdated position to the check. - - also, the checking node did not use OVERLA_RADIUS_MULTIPLIER when - doing the check. - -DEBUG: do neighbor discovery check after processing all the MOVE messages, -(big) this does improve consistency. (drift distance has decreased - dramatically) - - -2005/05/25 (3) --------------- -- goal: debug inconsistency problem - -DEBUG found out the weird incorrect EN-judgement in some test case - was caused by when processing a NODE message, we may insert the new - node to Voronoi first then remove it. However, this practice - could redundently insert and remove from the Voronoi class when - the NODE messsage contains an already-known node. - after fixing this bug only minor inconsistency occur (fixed within - the next step). - -DEBUG with some tweaking consistency can now bring to above 99.97% for - up to 30 noes - -ideas: - two possibility for the low consistency: - - the new event model makes neighbor discovery slower (at least - one round is required to learn), this could explain many of - the inconsistency that fixes itself after one step - - - when processing message, if we immediately respond to MOVE - and respond with NODE, it might not give the most update/current - view, as subsequent MOVE command could change the Voronoi picture. - This can be fixed by first do all the movements, then respond - with NODE in a collective manner. this might explain the slight - drop of consistency in the basic model when # of nodes is high. - -2005/05/16 (1) --------------- -- goal: debug inconsistency problem - - -2005/05/15 (7) --------------- -- goal: debug inconsistency problem - -DEBUG woke up and realize the inconsistency problem could be due to - that i've combined the new AOI and new EN neighbor discovery - together in the current VAST (as opposed to using separate list - in VON). however, later found out the current inconsistency problem - is likely caused by incorrect judgement of EN (still to be investigate) - -2005/05/13 (5) --------------- -- goal: debug inconsistency problem - -DEBUG solve a little bug where there are some "zombie" nodes that just - stand still. found out the problem was that delete_node() performs - checks whether the node to be deleted is an enclosing neighbor. - Actually it need not check (but this also tells us that, it's possible - that A disconnects B because A thinks B no longer overlaps and is - not an enclosing neighbor, yet A could still be an EN to B - (this could a problem.. isn't it?) - -2005/05/12 (4) --------------- -- goal: 1. record/playback mechanism (done) - 2. low inconsistency debug - -- print results to log files (15:18) - - - -2005/05/11 (3) --------------- -- goal: move to linux platform and run test simulations (17:40) - find out about the low consistency problem - -BUG: discover the problem from yesterday was that the math and C++ libraries - do not seem to link right. (even a small test program that calls - sqrt() cannot be compiled) - - solution: use 'g++' for compliation, and add "-lm" when trying to - link against the math library when using "gcc" - -BUG: incorrect results for 500 nodes, 1000 nodes would crash after step 2 - - -2005/05/10 (2) --------------- -- goal: move to linux platform and run test simulations - find out about the low consistency problem - -BUG: while trying to port VAST to linux, met with this annoying bug: - -console.cpp: In function `int main(int, char**)': -console.cpp:52: use of `id_t' is ambiguous -/usr/include/sys/types.h:105: first declared as `typedef __id_t id_t' here -../../include/typedef.h:15: also declared as `typedef long unsigned int - VAST::id_t' here -console.cpp:52: `id_t' denotes an ambiguous type -console.cpp:52: confused by earlier errors, bailing out - - basically it says that 'id_t' has already being defined in "types.h" - therefore there's a double-definition problem. - - solution: use a work-around by avoiding the use of id_t in all - client programs. (for example, in statistics.h, I changed - map into map - -- got both /src and /src_sim to compile into linux shared library (.so format) - successfully, however, wasn't able to compile console.cpp properly - (appearantly due to linker problems where simple methods such as delete[] - and new could not be found.. a library path problem?) - - - - - -2005/05/09 (1) --------------- -- goal: 1. implement connection beyond conn_limit restrictions (16:43) - 2. finish stat collection code (21:30) - 3. move to linux platform and run test simulations - -- both console and gui mode can run fairly smoothly now, and produce - good results. now the main problem visible is the dropped - consistency (reason unknown) - - - -2005/05/07 (6) --------------- -- goal: 1. debug vast_dc using GUI - 2. move to Linux box, run simulation that collects results - -BUG: inconsistency of neighbor position occurs (esp. with large # of neighbors) - possible cause: - When A tries to connect to B, B refuses (out of its - own AOI). however, A is not notified so will keep "zombie" - node in its local view. - (however, problem persists when refusal mechanism is removed) - -DEBUG: the above bug was somehow solved by modifying the following: - 1) remove the overlap check in insert_node (so that insertion only - checks if it was already there) - 2) when responding to an EN with NODE, only relevant ENs - (i.e. those overlapped with the moving node) are sent back - (previously ALL missing neighbors are sent, therefore making - it necessary for insert_node to check if it was necessary - to connect upon receiving NODE, now we just accept whatever's - notified by NODE unconditionally) - - some "slight" inconsistency is still observed when a new node is - discovered, however, it's usually quickly fixed when the moving node's - next position update is received (reason unknown). - -IDEA: define a new 'topology consistency' matrics: - should take into account difference between observed & unobserved - (mix 'consistency' with 'drift distance') consisteny is weighted - against difference in coordinates - -IDEA: different types of disconnection needs to be categorized: - - Scenario Response Message - ------------------------------------------------------------------- - 1. leaving overlay reaction not needed DISCONNECT - 2. AOI no longer overlap reaction not needed (?) DISCONNECT - 3. refuse connection adjust AOI OVERCAP - 4. shrink AOI adjust AOI OVERCAP - -TODO: joiner exceeds connection limit should be taken care - -IDEA: title for a new paper: -(big) "Spatial and Temporal Locality-based Visibility" or - "Scalalbe Visibility based on Spatial and Temporal Locality" - - idea is simple: how do you see "more" in AOI, for a given amount of - bandwidth, yet require DIRECT connection among all nodes? - - Answer: you set up different update frequency with each sender. - sort of like using a big pipe for more nearer sender, and smaller pipe - for further sender. But the combined size of pipes are the same, - and if you extend more and more (receiving from more and more users), - the pipes will become smaller and smaller. If done well, this could - bring very nice "granuality" into update frequency and potentially - see large number of users, and can even individually priortize - update frequency. - - -BUG: after implementing pieces of the 'statistics' class. - program just halts/crashes for no appearant reason, even simple - console mode wouldn't work. - -DEBUG: found out it was a small "fix" in net_emu.cpp that caused the problem - (and I even removed the whole new 'statistics' class!) - - // failure likely due to memory allocation problem - if (newnode->size == 0) - return 0; - - it skipped the proper insertion to message queue, ignoring the fact - that size of 0 can in fact be legal. - - -2005/05/06 (5) --------------- -- goal: finished debugging the networked layer - - -BUG: very weird bug in send_nodes () - had tried to use various methods in the map _id2node - for example: - - map::iterator> _id2node; - map _id2node; - - but when trying to access a particular node in send_nodes () - the node access, whether using iterator or Node * would - give very strange/incorrect data for a particular node_id - -DEBUG: possible cause for the above: - after insertion to a std::vector, the ordering will change - (i.e. cannot assume the first inserted can be accessed with index 0) - - solution: change the types of _neighbors and _id2node to - - std::vector _neighbors; - map _id2node; - -DEBUG: found out a major bug left from previous implementation of VON -(big) it is possible that a remote node has just been disconnected, - yet it has sent MOVE messages to me. therefore when trying - to process this MOVE message, the remote list's EN list would - not be found. It was worked around by 'new' an EN list, however - the real solution should be simply to ignore the MOVE message - -- finished networking layer debug, also created preliminary GUI interface - (global & switchable local view, AOI-radius display, Voronoi edges) - - -2005/05/05 (4) --------------- -- goal: finished debugging the networked layer - -DEBUG: the crash when calling vast_dc destructor bug was caused by - incorrect use of iterator - - in insert_node() the code was - - _neighbors.push_back (node); - _id2node[node.id] = _neighbors.end (); - - // types - std::vector _neighbors; - map::iterator> _id2node; - - the proper way should be: - - _id2node[node.id] = _neighbors.insert (_neighbors.end (), node); - - -2005/05/03 (2) --------------- -- goal: finished implementing the networked layer -- implemented but buggy (crash when destructor of vast_dc is called) - - - -2005/05/02 (1) --------------- -- goal: implement the networked layer - -some ideas: A universal (for both emulation and actual network) network - interface for the protocol layer to use (class 'network') - - In emulation, there'll be a shared node discoverer object - (class 'netemu') used to find pointer to different 'network' - objects to send messages to. each network object maintains - separate "receive queue" - - when network object is first used, it would obtain a temp. - id from the discoverer object, until it gets its unique ID - from gateway and register with the discoverer object - -- had done a preliminary compilable version of a switchable network layer - - -2005/04/29 (5) --------------- -- goal: implement the networked layer - - -2005/04/28 (4) --------------- -- goal: implement the networked layer - - -2005/04/19 (2) --------------- -- goal: implement a simulated network and have some actual simulation runs - - -2005/04/18 (1) --------------- -- goal: implement VAST DC model as much as possible - -- finished a prelimiary implementation of vast_dc.cpp - now onto network.cpp - -2005/04/16 (6) --------------- -- goal: implement VAST DC model as much as possible - - -2005/04/15 (5) --------------- -- goal: implement VAST DC model as much as possible - -- implemented processmsg() all messages except MOVEs - -2005/04/14 (4) --------------- -- goal: implementation of the direct connection model and - simulated network layers? (miracle required.. :) - -- create a factory class for vast called 'metaverse', which offers - create() and destory() - -2005/04/13 (3) --------------- -- goal: implementation of the direct connection model and - simulated network layers. - -- separated vast.dll and VASTsim.dll developments, an outside app that - uses VASTsim.dll will only need to include VASTsim.h, provided - that the directory of /src and /src_sim are located in the same place. - (better way to do it?) - - -2005/04/12 (2) --------------- -- goal: create workable GUI suitable for displaying - - global view of nodes (check) - - local view of a particular node - - regular refresh/updates (check) - - view-pan/move - - enlarge/smaller (optional?) - -- studied DX9 in the morning, but decided to use GDI instead. - got a working version. also created the VASTsim.dll library apart from - vast.dll - -2005/04/11 (1) --------------- -- goal: define interface and code structure - -- defined vast.h main interface used by outside app (virtual class) - vast_dc.h direct connection model implementation - typedef.h main data structures - network.h network layer - config.h platform-specific configurations - Voronoi.h Voronoi services - SFVoronoi.h Steve Fortune Voronoi - - vast_sim.cpp a simulator - behavior.h behavior model (random movements) - -2005/04/10 (7) --------------- -- start log for VAST - -VAST: VON-based Adaptive Scalable Toolkit - - - a light-weight C++ library to support VON - - three layers: 1) simulator 2) protocol 3) network, each is substitutable - - well-defined interface, easy to use - - - clean, fast, solid - - ACE is confined to network layer - -- goal today: define interface and code structure - - - diff --git a/VAST.js b/VAST.js index edd7bf23..09b1d8d9 100644 --- a/VAST.js +++ b/VAST.js @@ -17,15 +17,16 @@ LOG.setLevel(3); // // VAST & VON // -// ID definitions -global.VAST_ID_UNASSIGNED = 0; -global.VAST_ID_GATEWAY = 1; global.VAST = require('./vast_types'); global.VAST.net = require('./net/vast_net'); global.VAST.client = require('./VAST'); global.VAST.matcher = require('./VAST_matcher'); +// ID definitions +global.VAST.ID_UNASSIGNED = 0; +global.VAST.ID_GATEWAY = 1; + // TODO: find a better way to store this? (maybe in msg_handler?) global.VAST.state = { ABSENT: 0, diff --git a/VAST_matcher.js b/VAST_matcher.js index f7a85dbd..fa037dd8 100644 --- a/VAST_matcher.js +++ b/VAST_matcher.js @@ -81,13 +81,13 @@ function VAST_matcher(recv_callback, settings) { var _sendGatewayMessage = function (pack) { pack.targets = []; - pack.targets.push(VAST_ID_GATEWAY); + pack.targets.push(VAST.ID_GATEWAY); _sendPack(pack, true); } // check whether current node is a gateway var _isGateway = function () { - return (_self.getSelf().id === VAST_ID_GATEWAY); + return (_self.getSelf().id === VAST.ID_GATEWAY); } // @@ -343,7 +343,7 @@ function VAST_matcher(recv_callback, settings) { // function to create a new net layer this.init = function (self_id, port, done_CB) { - self_id = self_id || VAST_ID_UNASSIGNED; + self_id = self_id || VAST.ID_UNASSIGNED; port = port || VAST_DEFAULT_PORT; // create message handler manager and add self as one of the handlers diff --git a/VON_peer.js b/VON_peer.js index 3182b043..e80f196e 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -147,7 +147,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // function to create a new net layer this.init = function (self_id, port, done_CB) { - self_id = self_id || VAST_ID_UNASSIGNED; + self_id = self_id || VAST.ID_UNASSIGNED; port = port || VON_DEFAULT_PORT; // create new layer @@ -206,7 +206,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // NOTE: need to do it here, as _storeMapping is not effective after addHandler calls initStates var addr = new VAST.addr(); addr.parse(GW_addr); - _storeMapping(VAST_ID_GATEWAY, addr); + _storeMapping(VAST.ID_GATEWAY, addr); LOG.warn('gateway set to: ' + addr.toString()); // store initial aoi @@ -220,8 +220,8 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // if id is empty, send PING to gateway to learn of my id first // otherwise attempt to join by contacting gateway - if (_getID() === VAST_ID_UNASSIGNED) - _sendMessage(VAST_ID_GATEWAY, VON_Message.VON_PING, {request: true}, VAST.priority.HIGHEST); + if (_getID() === VAST.ID_UNASSIGNED) + _sendMessage(VAST.ID_GATEWAY, VON_Message.VON_PING, {request: true}, VAST.priority.HIGHEST); else _setInited(); } @@ -593,11 +593,11 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // TODO: doesn't look clean, can gateway still send query request to itself? // that'll be a more general process // (however, will deal with how to determined 'already joined' for gateway) - if (_self.id === VAST_ID_GATEWAY) + if (_self.id === VAST.ID_GATEWAY) _setJoined(); else // send out query request first to find acceptor - _query(VAST_ID_GATEWAY, _self.aoi.center, VON_Message.VON_JOIN, _self); + _query(VAST.ID_GATEWAY, _self.aoi.center, VON_Message.VON_JOIN, _self); } // set current node to be 'joined' @@ -985,7 +985,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { var _assignNewID = function () { // we use our own ID as first - // NOTE if we start with VAST_ID_UNASSIGNED (0) then first ID will be 1 + // NOTE if we start with VAST.ID_UNASSIGNED (0) then first ID will be 1 if (_new_ID === undefined) _new_ID = _getID() + 1; @@ -1010,7 +1010,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { } */ - if (_self.id === VAST_ID_UNASSIGNED && pack.type !== VON_Message.VON_PING) { + if (_self.id === VAST.ID_UNASSIGNED && pack.type !== VON_Message.VON_PING) { LOG.error('VON_peer: node not yet init (got unique ID), should not process any messages'); return false; } @@ -1031,7 +1031,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // check if this is the first ever ID assigned by me // if so, then I'm likely the gateway (my ID is also unassigned yet) - if (_self.id === VAST_ID_UNASSIGNED) { + if (_self.id === VAST.ID_UNASSIGNED) { LOG.warn('first ID assigned, likely I am the gateway'); _self.id = remote_id; } @@ -1040,7 +1040,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _sendMessage(from_id, VON_Message.VON_PING, {request: false, aid: remote_id}, VAST.priority.HIGH, true); } // otherwise we got a response from gateway, set my ID, can now send join request - else if (_self.id === VAST_ID_UNASSIGNED) { + else if (_self.id === VAST.ID_UNASSIGNED) { var assigned_id = parseInt(pack.msg.aid); LOG.debug('assigned_id: ' + assigned_id); _setID(assigned_id); diff --git a/VSS/VSS.js b/VSS/VSS.js index c6f4b623..88a03706 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -1,6 +1,6 @@ // include VAST -require('../common'); +require('../VAST'); // do not show debug //LOG.setLevel(2); diff --git a/VSS/handler.js b/VSS/handler.js index d6de5874..822ea523 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -4,7 +4,7 @@ // // include VAST -require('../common'); +require('../VAST'); // default port for connecting / creating VON nodes var _VON_port = 37700; @@ -52,7 +52,7 @@ var _registerNode = function (pos, info, done_CB) { var new_node = new VON.peer(); // join in the network - new_node.init(VAST_ID_UNASSIGNED, ip_port.port + _nodes_created, function () { + new_node.init(VAST.ID_UNASSIGNED, ip_port.port + _nodes_created, function () { _nodes_created++; diff --git a/VSS/SFVoronoi.java b/misc/SFVoronoi.java similarity index 100% rename from VSS/SFVoronoi.java rename to misc/SFVoronoi.java diff --git a/common_util.js b/misc/common_util.js similarity index 100% rename from common_util.js rename to misc/common_util.js diff --git a/generic_net.js b/misc/generic_net.js similarity index 100% rename from generic_net.js rename to misc/generic_net.js diff --git a/hash.js b/misc/hash.js similarity index 100% rename from hash.js rename to misc/hash.js diff --git a/stable-proxy.js b/misc/stable-proxy.js similarity index 100% rename from stable-proxy.js rename to misc/stable-proxy.js diff --git a/tools.js b/misc/tools.js similarity index 100% rename from tools.js rename to misc/tools.js diff --git a/net/vast_net.js b/net/vast_net.js index df57ab1b..4878e79e 100644 --- a/net/vast_net.js +++ b/net/vast_net.js @@ -62,9 +62,10 @@ 2012-07-05 first working version (storeMapping, switchID, send) 2012-07-20 rename switchID -> setID (can set self ID) */ + var os = require('os'); var l_net = require('./net_nodejs'); // implementation-specific network layer -//var VAST_ID_UNASSIGNED = 0; +//var VAST.ID_UNASSIGNED = 0; // // input: @@ -112,7 +113,7 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { // // id for myself, created by an id_generator, if available - var _self_id = id || VAST_ID_UNASSIGNED; + var _self_id = id || VAST.ID_UNASSIGNED; // mapping between id and address // TODO: clear mapping once in a while? (for timeout mapping) @@ -143,7 +144,7 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { // simply replace any existing mapping _id2addr[id] = addr; } - + // switch an existing id to socket mapping var l_setID = this.setID = function (new_id, old_id) { @@ -181,7 +182,7 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { // no messages to send if (_msgqueue.hasOwnProperty(id) === false || _msgqueue[id].length === 0) - return; + return false; // if target id does not exist, indicate error if (_sockets.hasOwnProperty(id) === false) { @@ -533,7 +534,7 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { // NOTE: this check should be performed only once // NOTE: as this check is before receiving new ID from remote host, // the first node joining the system will be given the gateway's ID (1) - if (remote_id < VAST_ID_UNASSIGNED) { + if (remote_id < VAST.ID_UNASSIGNED) { var sender_id = parseInt(pack.src); @@ -541,7 +542,11 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { // if ID exists, then there's already an established connection if (_sockets.hasOwnProperty(sender_id) === true) { - LOG.warn('[' + _self_id + '] redundent socket already exists: ' + sender_id); + var size = Object.keys(_sockets).length; + LOG.warn('[' + _self_id + '] redundent socket already exists: ' + sender_id + ' sock size: ' + size); + for (var sock_id in _sockets) + LOG.warn(sock_id); + // disconnect remote host // however, message still needs to deliver @@ -549,14 +554,16 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { } // store the remote ID as remote host's socket ID - else if (sender_id !== VAST_ID_UNASSIGNED) { + else if (sender_id !== VAST.ID_UNASSIGNED) { l_setID(sender_id, socket.id); remote_id = sender_id; - } + } } // pass message to upper layer for handling - if (typeof onReceive === 'function') { + if (typeof onReceive === 'function') { + LOG.warn('[' + _self_id + '] process pack by upper layer from [' + remote_id + ']: '); + LOG.warn(pack); // TODO: queue-up message to be processed later? onReceive(remote_id, pack); } diff --git a/scale.bat b/scale.bat deleted file mode 100644 index 214dbc5e..00000000 --- a/scale.bat +++ /dev/null @@ -1 +0,0 @@ -node test_VON_scale 37700 %1 diff --git a/test/client.bat b/test/client.bat new file mode 100644 index 00000000..868c3e2a --- /dev/null +++ b/test/client.bat @@ -0,0 +1 @@ +node test_von_peer 127.0.0.1:37700 diff --git a/gw.bat b/test/gw.bat similarity index 100% rename from gw.bat rename to test/gw.bat diff --git a/test/scale.bat b/test/scale.bat new file mode 100644 index 00000000..d468d880 --- /dev/null +++ b/test/scale.bat @@ -0,0 +1 @@ +node test_VON_scale 127.0.0.1:37700 %1 diff --git a/test/test_VAST_client.js b/test/test_VAST_client.js index 9038907d..56e3da03 100644 --- a/test/test_VAST_client.js +++ b/test/test_VAST_client.js @@ -52,7 +52,7 @@ var aoi = new VAST.area(new VAST.pos(x, y), 100); var moveAround = function () { // move if not GW - if (client.getSelf().id !== VAST_ID_GATEWAY) { + if (client.getSelf().id !== VAST.ID_GATEWAY) { // random walk new location (5 units within current center position) aoi.center.x += Math.floor((Math.random()%10 - 5)); aoi.center.y += Math.floor((Math.random()%10 - 5)); @@ -68,7 +68,7 @@ var moveAround = function () { var interval_id = undefined; // after init the client will bind to a local port -client.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.port, function () { +client.init((is_client ? VAST.ID_UNASSIGNED : VAST.ID_GATEWAY), gateway_addr.port, function () { LOG.warn('test_VAST_client: init done'); @@ -80,7 +80,7 @@ client.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.por /* // try to move around once in a while... (if not gateway) - if (id !== VAST_ID_GATEWAY) { + if (id !== VAST.ID_GATEWAY) { interval_id = setInterval(function(){ moveAround() }, 1000); } */ diff --git a/test/test_VON_peer.js b/test/test_VON_peer.js index 468fdf40..d139a690 100644 --- a/test/test_VON_peer.js +++ b/test/test_VON_peer.js @@ -49,7 +49,7 @@ var aoi = new VAST.area(new VAST.pos(x, y), 100); var moveAround = function () { // move if not GW - if (peer.getSelf().id !== VAST_ID_GATEWAY) { + if (peer.getSelf().id !== VAST.ID_GATEWAY) { // random walk new location (5 units within current center position) aoi.center.x += Math.floor((Math.random()%10) - 5); aoi.center.y += Math.floor((Math.random()%10) - 5); @@ -71,7 +71,7 @@ var moveAround = function () { var interval_id = undefined; // after init the peer will bind to a local port -peer.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.port, function () { +peer.init((is_client ? VAST.ID_UNASSIGNED : VAST.ID_GATEWAY), gateway_addr.port, function () { peer.join(gateway_addr, aoi, @@ -80,7 +80,7 @@ peer.init((is_client ? VAST_ID_UNASSIGNED : VAST_ID_GATEWAY), gateway_addr.port, LOG.warn('joined successfully! id: ' + id + '\n'); // try to move around once in a while... (if not gateway) - if (id !== VAST_ID_GATEWAY) { + if (id !== VAST.ID_GATEWAY) { interval_id = setInterval(function(){ moveAround() }, 1000); } } diff --git a/test/test_VON_scale.js b/test/test_VON_scale.js index 0f8daa2b..053e6af4 100644 --- a/test/test_VON_scale.js +++ b/test/test_VON_scale.js @@ -70,7 +70,7 @@ var VONnode = function (num, GWaddr, radius) { peer.move(aoi); } - peer.init(VAST_ID_UNASSIGNED, port, function () { + peer.init(VAST.ID_UNASSIGNED, port, function () { peer.join(GWaddr, aoi, @@ -78,7 +78,7 @@ var VONnode = function (num, GWaddr, radius) { function (id) { LOG.warn('joined successfully! id: ' + id + '\n'); - if (id !== VAST_ID_GATEWAY) + if (id !== VAST.ID_GATEWAY) setInterval(moveNode, tick_interval); } ); diff --git a/vast.io/vast.io-server.js b/vast.io/vast.io-server.js index bb24170d..9091a16f 100644 --- a/vast.io/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -6,7 +6,7 @@ process.on('uncaughtException', function(err) { */ // include VAST -require('../common'); +require('../VAST'); // show only warnings LOG.setLevel(2); @@ -79,13 +79,13 @@ io.sockets.on('connection', function (socket) { aoi.parse(data.aoi); // create GW or a connecting client - // NOTE: by using VAST_ID_UNASSIGNED as default, the first created node will be the gateway + // NOTE: by using VAST.ID_UNASSIGNED as default, the first created node will be the gateway _self = new VON.peer(); nodes_created++; // join in the network - _self.init(VAST_ID_UNASSIGNED, ip_port.port + nodes_created, function () { + _self.init(VAST.ID_UNASSIGNED, ip_port.port + nodes_created, function () { _self.join(ip_port, aoi, From 5e4bfba9d2e2af12d6c1e3e6a4b934e73fc413ca Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Wed, 13 Mar 2013 17:55:42 +0800 Subject: [PATCH 022/141] VSS publish_pos returns new_list/left_list/subscribers --- VAST.js | 7 + VSS/VSS.js | 16 +- VSS/handler.js | 327 ++++++++------------------------------ VSS/logic.js | 270 +++++++++++++++++++++++++++++++ common/logger.js | 15 +- {VSS => misc}/line2d.java | 0 net/net_nodejs.js | 93 ++++++----- net/test_vast_net.js | 2 +- net/vast_net.js | 54 ++++--- test/test_VAST_client.js | 2 +- test/test_VON_peer.js | 4 +- test/test_VON_scale.js | 2 +- test/test_net_nodejs.js | 2 +- vast.io/vast.io-server.js | 4 +- 14 files changed, 454 insertions(+), 344 deletions(-) create mode 100644 VSS/logic.js rename {VSS => misc}/line2d.java (100%) diff --git a/VAST.js b/VAST.js index 09b1d8d9..3cf24d73 100644 --- a/VAST.js +++ b/VAST.js @@ -59,3 +59,10 @@ global.VAST.msgstr = [ global.VON = { peer: require('./VON_peer') }; + +// configurable settings +global.VAST.Settings = { + port_gateway: 37700, + IP_gateway: '127.0.0.1' + +}; diff --git a/VSS/VSS.js b/VSS/VSS.js index 88a03706..7e58ec5f 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -5,15 +5,17 @@ require('../VAST'); // do not show debug //LOG.setLevel(2); -var server = require("./server"); -var router = require("./router"); +var server = require("./server"); +var router = require("./router"); var handlers = require("./handler"); +/* var handle = {} -handle["publish"] = handlers.publish; -handle["subscribe"] = handlers.subscribe; +handle["publish"] = handlers.publish; +handle["subscribe"] = handlers.subscribe; handle["unsubscribe"] = handlers.unsubscribe; -handle["query"] = handlers.query; -handle["revoke"] = handlers.revoke; +handle["query"] = handlers.query; +handle["revoke"] = handlers.revoke; +*/ -server.start(router.route, handle); \ No newline at end of file +server.start(router.route, handlers); \ No newline at end of file diff --git a/VSS/handler.js b/VSS/handler.js index 822ea523..e4263254 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -1,134 +1,13 @@ // -// local private variables to keep track of created nodes +// handler.js handles REST request and pass to logic to handling // -// include VAST -require('../VAST'); - -// default port for connecting / creating VON nodes -var _VON_port = 37700; -var _VON_gateway = '127.0.0.1'; -//var _VON_gateway = 'dev.imonology.com'; - -// default radius when first joined -// TODO: need to have default radius? -var _default_radius = 0; - -// number of nodes already created -var _nodes_created = 0; - -// API keys -> layers -> nodes mapping -// NOTE: it's a 3-dimensional array -var _keys = {}; - -// node id -> ident mapping -var _id2ident = {}; - +var logic = require("./logic"); // -// actual execution code +// helpers // -var _publishPos = function (node, pos, radius) { - - // build new AOI info - var aoi = new VAST.area(pos, radius); - - // perform movement - node.move(aoi); -} - -// -// helper code -// - -var _registerNode = function (pos, info, done_CB) { - - // extract AOI for the VON node to create - var aoi = new VAST.area(pos, _default_radius); - - // specify where to locate VON gateway - var ip_port = new VAST.addr(_VON_gateway, _VON_port); - var new_node = new VON.peer(); - - // join in the network - new_node.init(VAST.ID_UNASSIGNED, ip_port.port + _nodes_created, function () { - - _nodes_created++; - - // store node ident for ident discovery across different VSS servers - var ident_info = { - apikey: info.apikey, - layer: info.layer, - ident: info.ident - }; - - new_node.put(ident_info); - - new_node.join(ip_port, aoi, - - // done callback - function (self_id) { - - LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + new_node.getSelf().id); - - // keep track of newly joined node in internal record - if (_keys.hasOwnProperty(info.apikey) === false) - _keys[info.apikey] = {}; - if (_keys[info.apikey].hasOwnProperty(info.layer) === false) - _keys[info.apikey][info.layer] = {}; - - _keys[info.apikey][info.layer][info.ident] = new_node; - - // store id to ident mapping - LOG.debug('store mapping for node id: ' + self_id + ' ident: ' + info.ident); - _id2ident[self_id] = info; - - //new_node.put(info); - - // check content - LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); - - // perform initial publish pos - // (so we can get a list of neighbors) - // NOTE: doesn't appear to work... - //_publishPos(new_node, info, new_node.getSelf().aoi.radius); - - done_CB(new_node); - } - ); - }); -} - -var _revokeNode = function (info, done_CB) { - - // check if node exists, return error if not yet exist (need to publishPos first) - var node = _getNode(info); - - if (node === undefined || node === null) - return done_CB(false); - - var node_id = node.getSelf().id; - - //destroy node - node.leave(); - node.shut(); - node = null; - - delete _keys[info.apikey][info.layer][info.ident]; - - // check if we need to remove layer and/or API key - if (Object.keys(_keys[info.apikey][info.layer]).length === 0) - delete _keys[info.apikey][info.layer]; - if (Object.keys(_keys[info.apikey]).length === 0) - delete _keys[info.apikey]; - - // remove id to ident mapping - LOG.debug('remove mapping for node id: ' + node_id + ' ident: ' + info.ident); - delete _id2ident[node_id]; - - done_CB(true); -} // send back response to client var _reply = function (res, res_obj) { @@ -148,109 +27,30 @@ var _reply = function (res, res_obj) { } // send back subscriber list to client -var _replySubscribers = function (request, res) { +var _replyPublishPos = function (request, res) { - var node = _getNode(request, res); + var node = logic.getNode(request, res); - if (node !== undefined && node !== null) { - - LOG.debug('replySubscribers called, node id: ' + node.getSelf().id); - - // get a list of current neighbors's id - var neighbors = node.list(); - var list = []; - - var self = node.getSelf(); - - // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) - for (var id in neighbors) { - - LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); - - // convert node id to node ident (only for those registered here) - // NOTE: current approach can only do ident translation for nodes created via this VSS server - - var neighbor = neighbors[id]; - - //for (var i in neighbor) - // LOG.debug(i + ': ' + typeof neighbor[i]); - - // get node ident (from either 'meta' field or from mapping) - var info = undefined; - if (neighbor.hasOwnProperty('meta')) - info = neighbor.meta; - else if (_id2ident.hasOwnProperty(id)) - info = _id2ident[id]; - - // do not return a node if: - // 1. is self - // 2. no mapping for ident (the node is not created via VSS) - // 3. is not a subscriber to myself (subscribed area does not cover me) - if (self.id == id || - info === undefined || - _isSubscriber(neighbors[id], self.aoi) === false) - continue; - - list.push(info.apikey + ':' + info.layer + ':' + info.ident); - } - - // return success - var error = []; - _reply(res, [list, error]); - } -} + if (node === undefined || node === null || res === undefined) { + LOG.error('node not found or response object invalid', 'replyPublishPos'); + return; + } + LOG.debug('replySubscribers called, node id: ' + node.getSelf().id); -// get a specific node given its API key, layer, and node ident -// returns 'undefined' if key info is missing -// returns 'null' if not found -var _getNode = function (req, res) { - - // check if any essential info is missing - if (req.apikey === '' || req.layer === '' || req.ident === ''){ - if (res !== undefined) - _reply(res, ["", ["key info (apikey/layer/ident) missing"]]); - return undefined; - } - - for (var key in _keys) - LOG.debug('key: ' + key); - - if (_keys.hasOwnProperty(req.apikey)) { - var layers = _keys[req.apikey]; - - for (var layer in layers) - LOG.debug('layer: ' + layer); - - if (layers.hasOwnProperty(req.layer)) { - var nodes = layers[req.layer]; - - for (var node in nodes) - LOG.debug('node: ' + node); - - if (nodes.hasOwnProperty(req.ident)) { - return nodes[req.ident]; - } - } - } - - // no node found, warn in advance - if (res !== undefined) - _reply(res, ["", ['node not yet created']]); - - return null; + // list to be returned (subscribers, new neighbors, left neighbors) + var lists = logic.getLists(node); + + // return success + var error = []; + _reply(res, [lists, error]); } -// check if a given node is a direct area subscriber for a center point -var _isSubscriber = function (node, aoi) { +// +// public actions +// - // NOTE: if subscription radius is 0, then we consider it's not subscribing anything - var result = (node.aoi.radius != 0 && node.aoi.covers(aoi.center)); - LOG.debug('isSubscriber check if node ' + node.toString() + ' covers ' + aoi.center.toString() + ': ' + result); - return result; -} - -function publish(words, res) { +var publish = function (words, res) { LOG.debug("Request handler 'publish' was called."); var request = {}; @@ -265,7 +65,7 @@ function publish(words, res) { // extract parameters request = { apikey: words[3] || '', layer: words[4] || '', - ident: words[5] || '', + name: words[5] || '', x: Number(words[6]) || 0, y: Number(words[7]) || 0, z: Number(words[8]) || 0 @@ -276,41 +76,32 @@ function publish(words, res) { // check if node's already created, if so then send update // if not then need to create a new VON peer node - var node = _getNode(request); + var node = logic.getNode(request); switch (node) { // parameter invalid case undefined: { - _reply(res, ["", ["key info (apikey/layer/ident) missing"]]); + _reply(res, ["", ["key info (apikey/layer/name) missing"]]); break; } // no node exist, create new case null: { - // if node does not exist, create one - var pos = new VAST.pos(request.x, request.y); - - // append additional z value - pos.z = request.z; - - _registerNode(pos, request, function (new_node) { + + // if node does not exist, create one + logic.registerNode(request, request, function (new_node) { - LOG.debug('new_node: ' + JSON.stringify(new_node)); - - // send back node creation response - //_reply(res, ["OK", []]); - - _replySubscribers(request, res); + LOG.debug('new_node: ' + JSON.stringify(new_node)); + _replyPublishPos(request, res); }); break; } // publish pos default: { - _publishPos(node, request, node.getSelf().aoi.radius); + logic.publishPos(node, request, node.getSelf().aoi.radius); - //_reply(res, ["OK", []]); - _replySubscribers(request, res); + _replyPublishPos(request, res); break; } } @@ -329,7 +120,7 @@ function publish(words, res) { } } -function subscribe(words, res) { +var subscribe = function (words, res) { LOG.debug("Request handler 'subscribe' was called."); var request = {}; @@ -343,7 +134,7 @@ function subscribe(words, res) { // extract parameters request = { apikey: words[3] || '', layer: words[4] || '', - ident: words[5] || '', + name: words[5] || '', radius: Number(words[6]) || 0 }; @@ -355,11 +146,17 @@ function subscribe(words, res) { } // check if node exists, return error if not yet exist (need to publishPos first) - var node = _getNode(request, res); - - if (node != undefined && node !== null) { + var node = logic.getNode(request); + + if (node === undefined) { + _reply(res, ["", ["key info (apikey/layer/name) missing"]]); + } + else if (node === null) { + _reply(res, ["", ['node not yet created']]); + } + else { // update AOI radius for area subscription - _publishPos(node, node.getSelf().aoi.center, request.radius); + logic.publishPos(node, node.getSelf().aoi.center, request.radius); // return success _reply(res, ["OK", []]); @@ -373,7 +170,7 @@ function subscribe(words, res) { } } -function unsubscribe(words, res) { +var unsubscribe = function (words, res) { LOG.debug("Request handler 'unsubscribe' was called."); var request = {}; @@ -388,20 +185,26 @@ function unsubscribe(words, res) { // extract parameters request = { apikey: words[3] || '', layer: words[4] || '', - ident: words[5] || '' + name: words[5] || '' }; // check if node exists, return error if not yet exist (need to publishPos first) - var node = _getNode(request, res); - - if (node === null || node === undefined) + var node = logic.getNode(request); + + if (node === undefined) { + _reply(res, ["", ["key info (apikey/layer/name) missing"]]); + } + else if (node === null) { + _reply(res, ["", ['node not yet created']]); + } + else break; // update AOI radius for area subscription - _publishPos(node, node.getSelf().aoi.center, 0); + logic.publishPos(node, node.getSelf().aoi.center, 0); // return success - _reply(res, ["OK", []]); + _reply(res, ["OK", []]); break; } default: { @@ -412,7 +215,7 @@ function unsubscribe(words, res) { } -function query(words, res) { +var query = function (words, res) { LOG.debug("Request handler 'query' was called."); var request = {}; @@ -427,11 +230,11 @@ function query(words, res) { // extract parameters request = { apikey: words[3] || '', layer: words[4] || '', - ident: words[5] || '' + name: words[5] || '' }; // check if node exists, return error if not yet exist (need to publishPos first) - _replySubscribers(request, res); + _replyPublishPos(request, res); break; } @@ -443,7 +246,7 @@ function query(words, res) { } // remove a node from system -function revoke(words, res) { +var revoke = function (words, res) { LOG.debug("Request handler 'revoke' was called."); var request = {}; @@ -458,10 +261,10 @@ function revoke(words, res) { // extract parameters request = { apikey: words[3] || '', layer: words[4] || '', - ident: words[5] || '' + name: words[5] || '' }; - _revokeNode(request, function(result) { + logic.revokeNode(request, function(result) { // return success if (result === true) _reply(res, ["OK", []]); @@ -479,9 +282,9 @@ function revoke(words, res) { } -exports.publish = publish; -exports.subscribe = subscribe; +exports.publish = publish; +exports.subscribe = subscribe; exports.unsubscribe = unsubscribe; -exports.query = query; -exports.revoke = revoke; +exports.query = query; +exports.revoke = revoke; diff --git a/VSS/logic.js b/VSS/logic.js new file mode 100644 index 00000000..c5585ff7 --- /dev/null +++ b/VSS/logic.js @@ -0,0 +1,270 @@ + +// +// logic.js -- VSS core logic to maintain multiple VAST nodes +// +// history: +// 2013-03-12 separated from handler.js to be sharable +// + +// include VAST +require('../VAST'); + +// default port for connecting / creating VON nodes +var _VON_port = VAST.Settings.port_gateway; +var _VON_gateway = VAST.Settings.IP_gateway; + +// default radius when first joined +// TODO: need to have default radius? +var _default_radius = 0; + +// number of nodes already created +var _nodes_created = 0; + +// API keys -> layers -> nodes mapping +// NOTE: it's a 3-dimensional array +var _keys = {}; + +// node id -> ident mapping +var _id2ident = {}; + +// node id -> neighbor list mapping +var _id2neighbors = {}; + + +// +// public calls +// + +var _registerNode = exports.registerNode = function (position, info, done_CB) { + + var pos = new VAST.pos(position.x, position.y); + + // append additional z value + pos.z = position.z; + + // extract AOI for the VON node to create + var aoi = new VAST.area(pos, _default_radius); + + // specify where to locate VON gateway + var ip_port = new VAST.addr(_VON_gateway, _VON_port); + var new_node = new VON.peer(); + + // join in the network + new_node.init(VAST.ID_UNASSIGNED, ip_port.port + _nodes_created, function () { + + _nodes_created++; + + // store node ident for ident discovery across different VSS servers + var ident = { + apikey: info.apikey, + layer: info.layer, + name: info.name + }; + + // store as meta-data + new_node.put(ident); + + new_node.join(ip_port, aoi, + + // done callback + function (self_id) { + + LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + new_node.getSelf().id); + + // keep track of newly joined node in internal record + if (_keys.hasOwnProperty(info.apikey) === false) + _keys[info.apikey] = {}; + if (_keys[info.apikey].hasOwnProperty(info.layer) === false) + _keys[info.apikey][info.layer] = {}; + + _keys[info.apikey][info.layer][info.name] = new_node; + + // store id to ident mapping + LOG.debug('store mapping for node id: ' + self_id + ' name: ' + info.name); + _id2ident[self_id] = info; + + //new_node.put(info); + + // check content + LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); + + // perform initial publish pos + // (so we can get a list of neighbors) + // NOTE: doesn't appear to work... + //_publishPos(new_node, info, new_node.getSelf().aoi.radius); + + done_CB(new_node); + } + ); + }); +} + +var _revokeNode = exports.revokeNode = function (info, done_CB) { + + // check if node exists, return error if not yet exist (need to publishPos first) + var node = _getNode(info); + + if (node === undefined || node === null) + return done_CB(false); + + var node_id = node.getSelf().id; + + //destroy node + node.leave(); + node.shut(); + node = null; + + delete _keys[info.apikey][info.layer][info.name]; + + // check if we need to remove layer and/or API key + if (Object.keys(_keys[info.apikey][info.layer]).length === 0) + delete _keys[info.apikey][info.layer]; + if (Object.keys(_keys[info.apikey]).length === 0) + delete _keys[info.apikey]; + + // remove id to ident mapping + LOG.debug('remove mapping for node id: ' + node_id + ' name: ' + info.name); + delete _id2ident[node_id]; + + done_CB(true); +} + +// get a specific node given its API key, layer, and name (i.e., ident) +// returns 'undefined' if key info is missing +// returns 'null' if not found +var _getNode = exports.getNode = function (ident) { + + // check if any essential info is missing + if (ident.apikey === '' || ident.layer === '' || ident.name === '') + return undefined; + + for (var key in _keys) + LOG.debug('key: ' + key); + + if (_keys.hasOwnProperty(ident.apikey)) { + var layers = _keys[ident.apikey]; + + for (var layer in layers) + LOG.debug('layer: ' + layer); + + if (layers.hasOwnProperty(ident.layer)) { + var nodes = layers[ident.layer]; + + for (var node in nodes) + LOG.debug('node: ' + node); + + if (nodes.hasOwnProperty(ident.name)) { + return nodes[ident.name]; + } + } + } + + // no node found, warn in advance + return null; +} + +var _publishPos = exports.publishPos = function (node, pos, radius) { + + // build new AOI info + var aoi = new VAST.area(pos, radius); + + // perform movement + node.move(aoi); +} + +// get a list of subscribers, new neighbors, left neighbors +exports.getLists = function (node) { + + // get a list of current neighbors's id + var neighbors = node.list(); + var self = node.getSelf(); + + var new_list = []; + var left_list = []; + var subscribe_list = []; + + // list of current neighbors + var curr_neighbors = {}; + + // get previous neighbor list for this node, if available + var prev_neighbors = {}; + if (_id2neighbors.hasOwnProperty(self.id) === true) + prev_neighbors = _id2neighbors[self.id]; + + // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) + for (var id in neighbors) { + + LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); + + // convert node id to node ident (only for those registered here) + // NOTE: current approach can only do ident translation for nodes created via this VSS server + + var neighbor = neighbors[id]; + + //for (var i in neighbor) + // LOG.debug(i + ': ' + typeof neighbor[i]); + + // get node ident (from either 'meta' field or from mapping) + // 'meta' means remote, mapping means on this server (?) + var info = undefined; + if (neighbor.hasOwnProperty('meta')) + info = neighbor.meta; + else if (_id2ident.hasOwnProperty(id)) + info = _id2ident[id]; + + // skip if + // 1. is self + // 2. no mapping for ident (the node is not created via VSS) + if (self.id == id || + info === undefined) + continue; + + // build unique ident for this neighbor + var ident = info.apikey + ':' + info.layer + ':' + info.name; + + // check if this neighbor should be put to subscriber list + // is a subscriber to myself (i.e., subscribed area covers me) + if (_isSubscriber(neighbors[id], self.aoi)) + subscribe_list.push(ident); + + // check if I am a subscriber to this neighbor + if (_isSubscriber(self, neighbors[id].aoi)) { + + // store this neighbor to a map + curr_neighbors[id] = info; + + // check if this neighbor is new neighbor + if (prev_neighbors.hasOwnProperty(id) === false) + new_list.push(ident); + } + } + + // check if any previously known neighbor are no longer neighbors + for (var nid in prev_neighbors) { + if (curr_neighbors.hasOwnProperty(nid) === false) { + var info = prev_neighbors[nid]; + left_list.push(info.apikey + ':' + info.layer + ':' + info.name); + } + } + + // replace neighbor list for this node + _id2neighbors[self.id] = curr_neighbors; + + LOG.warn('neighbors returned. new: ' + new_list.length + + ' left: ' + left_list.length + ' subscribe: ' + subscribe_list.length); + + return [new_list, left_list, subscribe_list]; +} + +// +// helpers +// + +// check if a given node is a direct area subscriber for a center point +var _isSubscriber = function (node, aoi) { + + // NOTE: if subscription radius is 0, then we consider it's not subscribing anything + var result = (node.aoi.radius != 0 && node.aoi.covers(aoi.center)); + LOG.debug('isSubscriber check if node ' + node.toString() + ' covers ' + aoi.center.toString() + ': ' + result); + return result; +} diff --git a/common/logger.js b/common/logger.js index 7d7a46f2..7ed35ab4 100644 --- a/common/logger.js +++ b/common/logger.js @@ -17,9 +17,6 @@ var _ERREND = _white var _WARN = _yellow; function logger() { - - var currDate = new Date(); - var dateStr = '-' + currDate.getHours() + ':' + currDate.getMinutes() + '- '; // by default we display all var _level = 3; @@ -29,6 +26,12 @@ function logger() { return (typeof obj === 'object' ? JSON.stringify(obj, null, 4) : obj); } + // get current time + var _curr_time = function () { + var currDate = new Date(); + return '-' + currDate.getHours() + ':' + currDate.getMinutes() + '- '; + } + // set log level: 1 (error only), 2 (warning), 3 (debug) this.setLevel = function (level) { if (level <= 0 || level > 3) { @@ -42,19 +45,19 @@ function logger() { this.debug = function (msg) { msg = _convert(msg); if (_level >= 3) - console.log(dateStr + msg); + console.log(_curr_time() + msg); } this.warn = function (msg) { msg = _convert(msg); if (_level >= 2) - console.log(_WARN + dateStr + msg + _ERREND); + console.log(_WARN + _curr_time() + msg + _ERREND); } this.error = function (msg) { msg = _convert(msg); if (_level >= 1) - console.log(_ERR + dateStr + msg + _ERREND); + console.log(_ERR + _curr_time() + msg + _ERREND); } this.stack = function () { diff --git a/VSS/line2d.java b/misc/line2d.java similarity index 100% rename from VSS/line2d.java rename to misc/line2d.java diff --git a/net/net_nodejs.js b/net/net_nodejs.js index bdee0d28..096b1c97 100644 --- a/net/net_nodejs.js +++ b/net/net_nodejs.js @@ -35,6 +35,7 @@ onReceive(socket, msg) onConnect(socket) onDisconnect(socket) + onError(err) ///////////////////////////// supported functions: @@ -84,7 +85,7 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // reference var _that = this; - // check for connection validity and send to it) + // check for connection validity and send to it // ip_port is an object with 'host' and 'port' parameter this.connect = function (host_port) { @@ -94,19 +95,24 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // connect to the given host & port, while providing a connection listener _socket = l_net.createConnection(host_port.port, host_port.host); + //setupSocket(_socket, host_port); + + // NOTE: we need to specify 'connect' event handler here, as + // if it's specified in setupSocket, it may not respond / process the + // the event in time _socket.on('connect', function () { // setup connected socket // NOTE: the handler for 'connect' inside setupSocket won't be called, // as 'connect' event is already triggered here, so whatever task needs to be duplicated here // TODO: more elegant approach? - setupSocket(_socket); + setupSocket(_socket, host_port); - LOG.debug('out connect success for: ' + host_port.host + ':' + host_port.port); + LOG.debug('ooutt connect success for: ' + host_port.host + ':' + host_port.port); // store remote address & port - _socket.host = host_port.host; - _socket.port = host_port.port; + //_socket.host = host_port.host; + //_socket.port = host_port.port; _socket.disconnect = _that.disconnect; _socket.connected = true; @@ -114,18 +120,20 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { if (typeof onConnect === 'function') onConnect(_socket); }); + + /* // if there's error on the connection, 'close' will follow _socket.on('error', function(err){ LOG.error('out connect socket error: ' + err); }); - + */ } catch (e) { LOG.error('net_nodejs: connect error: ' + e); if (typeof onError === 'function') - onError('connect'); - } + onError('connect'); + } } // disconnect from a given socket id @@ -213,16 +221,40 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // setup a new usable socket with a socket handler //----------------------------------------- - var setupSocket = function (socket) { + var setupSocket = function (socket, host_port) { //LOG.debug('setupSocket called, onReceive: ' + typeof onReceive); + + // NOTE: this won't be triggered for out-going connections, only incoming (i.e., when a listening server calls this) + socket.on('connect', function () { - socket.setEncoding('UTF8'); - socket.setKeepAlive(true, 20 * 1000); // 20 seconds keepalive + // NOTE: remoteAddress & remotePort are available for incoming connection, but not outgoing sockets + //LOG.debug('connection created: ' + socket.remoteAddress + ':' + socket.remotePort); + + // if host_port is provided, it's an outgoing connect success + if (host_port !== undefined) { + socket.host = host_port.host; + socket.port = host_port.port; + LOG.warn('out connect success for ' + socket.host + ':' + socket.port); + } + else { + socket.host = socket.remoteAddress; + socket.port = socket.remotePort; + LOG.warn('in connect success for ' + socket.host + ':' + socket.port); + } + + socket.connected = true; + + // attach convenience function + socket.disconnect = function () { + socket.end(); + } + + // notify connection, pass the connecting socket + if (typeof onConnect === 'function') + onConnect(socket); + }); - // this is important to allow messages be delivered immediately after sending - socket.setNoDelay(true); - // call data callback to process data, if exists // (this happens when setupSocket is called by a listening server for setup new socket) if (typeof onReceive === 'function') { @@ -241,27 +273,6 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { } }); - // NOTE: this won't be triggered for out-going connections, only incoming (i.e., when a listening server calls this) - socket.on('connect', function () { - - // NOTE: remoteAddress & remotePort are available for incoming connection, but not outgoing sockets - //LOG.debug('connection created: ' + socket.remoteAddress + ':' + socket.remotePort); - - socket.host = socket.remoteAddress; - socket.port = socket.remotePort; - LOG.debug('in connect success for ' + socket.host + ':' + socket.port); - - socket.connected = true; - - // attach convenience function - socket.disconnect = function () { - socket.end(); - } - - // notify connection, pass the connecting socket - if (typeof onConnect === 'function') - onConnect(socket); - }); // handle connection error or close var disconnect_handler = function () { @@ -299,16 +310,22 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { socket.on('close', function (has_error) { //LOG.warn('socket [close] called'); // print error, if any - if (has_error) - LOG.error('socket close error: ' + has_error); + if (has_error) + LOG.error('socket close error: ' + has_error); disconnect_handler(); }); // if there's error on the connection, 'close' will follow socket.on('error', function (err){ - LOG.error('socket error: ' + err); + LOG.error('ssocket error: ' + err); }); + + socket.setEncoding('UTF8'); + socket.setKeepAlive(true, 20 * 1000); // 20 seconds keepalive + + // this is important to allow messages be delivered immediately after sending + socket.setNoDelay(true); return socket; } diff --git a/net/test_vast_net.js b/net/test_vast_net.js index 43dedde0..a7d8d925 100644 --- a/net/test_vast_net.js +++ b/net/test_vast_net.js @@ -12,7 +12,7 @@ var vast_net = require('./vast_net'); var net = undefined; // set default IP/port -var ip_port = {host: "127.0.0.1", port: 37}; +var ip_port = {host: VAST.Settings.IP_gateway, port: 37}; // get input IP / port // check port input, if any diff --git a/net/vast_net.js b/net/vast_net.js index 4878e79e..e6836a98 100644 --- a/net/vast_net.js +++ b/net/vast_net.js @@ -335,14 +335,19 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { // store message to queue // create queue for connection if not exist - if (_msgqueue.hasOwnProperty(id) === false) + if (_msgqueue.hasOwnProperty(id) === false) { + LOG.warn('msgqueue for [' + id + '] not exist, create one...'); _msgqueue[id] = []; - - _msgqueue[id].push(encode_str); - + } + + // store message to pending queue + _msgqueue[id].push(encode_str); + // if connections already exists, send directly, otherwise establish new connection - if (_sockets.hasOwnProperty(id)) + if (_sockets.hasOwnProperty(id)) { + LOG.warn('socket for [' + id + '] exists, send directly...'); l_sendPendingMessages(id); + } else { var str = ''; var unknown_count = 0; @@ -357,6 +362,8 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { LOG.warn('[' + _self_id + '] attempts to connect to [' + id + '] sock_size: ' + sock_size); //LOG.warn(str); } + + LOG.warn('socket for [' + id + '] not exiss, try to connect...'); l_connect(id); } } @@ -540,25 +547,26 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { LOG.debug('[' + _self_id + '] learns sender id: ' + sender_id); - // if ID exists, then there's already an established connection - if (_sockets.hasOwnProperty(sender_id) === true) { - var size = Object.keys(_sockets).length; - LOG.warn('[' + _self_id + '] redundent socket already exists: ' + sender_id + ' sock size: ' + size); - for (var sock_id in _sockets) - LOG.warn(sock_id); - - - // disconnect remote host - // however, message still needs to deliver - //socket.end(); - } - // store the remote ID as remote host's socket ID - else if (sender_id !== VAST.ID_UNASSIGNED) { - l_setID(sender_id, socket.id); - remote_id = sender_id; - } - } + if (sender_id !== VAST.ID_UNASSIGNED) { + + // if ID exists, then there's already an established connection + if (_sockets.hasOwnProperty(sender_id) === true) { + var size = Object.keys(_sockets).length; + LOG.warn('[' + _self_id + '] redundent socket already exists: ' + sender_id + ' sock size: ' + size); + for (var sock_id in _sockets) + LOG.warn(sock_id); + + // disconnect remote host + // however, message still needs to deliver + //socket.end(); + } + else + l_setID(sender_id, socket.id); + + remote_id = sender_id; + } + } // pass message to upper layer for handling if (typeof onReceive === 'function') { diff --git a/test/test_VAST_client.js b/test/test_VAST_client.js index 56e3da03..d1103c3a 100644 --- a/test/test_VAST_client.js +++ b/test/test_VAST_client.js @@ -17,7 +17,7 @@ require('../VAST'); // set default IP/port // set default IP/port -var gateway_addr = {host: "127.0.0.1", port: 37700}; +var gateway_addr = {host: VAST.Settings.IP_gateway, port: VAST.Settings.port_gateway}; var is_client = false; // IP/port diff --git a/test/test_VON_peer.js b/test/test_VON_peer.js index d139a690..9a3e7cbf 100644 --- a/test/test_VON_peer.js +++ b/test/test_VON_peer.js @@ -11,10 +11,10 @@ var AUTOMATIC_LEAVE_PERIOD = 3; // number of seconds require('../VAST'); // do not show debug -LOG.setLevel(2); +LOG.setLevel(3); // set default IP/port -var gateway_addr = {host: "127.0.0.1", port: 37700}; +var gateway_addr = {host: VAST.Settings.IP_gateway, port: VAST.Settings.port_gateway}; var is_client = false; // IP/port diff --git a/test/test_VON_scale.js b/test/test_VON_scale.js index 053e6af4..a1567b07 100644 --- a/test/test_VON_scale.js +++ b/test/test_VON_scale.js @@ -19,7 +19,7 @@ var node_speed = 5; var node_radius = 200; // set default IP/port -var gateway_addr = {host: "127.0.0.1", port: 37700}; +var gateway_addr = {host: VAST.Settings.IP_gateway, port: VAST.Settings.port_gateway}; // IP/port if (process.argv[2] !== undefined) { diff --git a/test/test_net_nodejs.js b/test/test_net_nodejs.js index a87f99ce..e3c2d0f3 100644 --- a/test/test_net_nodejs.js +++ b/test/test_net_nodejs.js @@ -42,7 +42,7 @@ var net = new net_nodejs( for (var i=0; i < process.argv.length; i++) console.log(i + ': ' + process.argv[i]); -var ip_port = {host: "127.0.0.1", port: 37}; +var ip_port = {host: VAST.Settings.IP_gateway, port: 37}; // check port input, if any var port = process.argv[2]; diff --git a/vast.io/vast.io-server.js b/vast.io/vast.io-server.js index 9091a16f..50932e25 100644 --- a/vast.io/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -46,7 +46,7 @@ process.on('uncaughtException', function(err) { var server_port = 38800; // default port for connecting / creating VON nodes -var VON_port = 37700; +var VON_port = VAST.Settings.port_gateway; var nodes_created = 0; @@ -72,7 +72,7 @@ io.sockets.on('connection', function (socket) { console.log(data); // specify where to locate VON gateway - var ip_port = new VAST.addr('127.0.0.1', VON_port); + var ip_port = new VAST.addr(VAST.Settings.IP_gateway, VON_port); // extract AOI for the VON node to create var aoi = new VAST.area(); From 9ea938dc22e8b3e4814178db101166691f8c4996 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 26 Mar 2013 23:39:06 +0800 Subject: [PATCH 023/141] VSS RPC mechanism first functional --- VAST.js | 3 +- VAST_matcher.js | 16 +- VON_peer.js | 38 ++-- VSS/VSS.js | 17 +- VSS/directory.js | 40 +++++ VSS/handler.js | 396 +++++++++++++++++++++++++++--------------- VSS/logic.js | 209 +++++++++++++--------- VSS/router.js | 99 +++++++++-- VSS/server.js | 58 +++++-- common/logger.js | 6 + common/util.js | 145 ++++++++++++++++ net/vast_net.js | 2 +- test/test_VON_peer.js | 24 +-- vast_voro.js | 2 +- 14 files changed, 745 insertions(+), 310 deletions(-) create mode 100644 VSS/directory.js diff --git a/VAST.js b/VAST.js index 3cf24d73..b94dde31 100644 --- a/VAST.js +++ b/VAST.js @@ -63,6 +63,5 @@ global.VON = { // configurable settings global.VAST.Settings = { port_gateway: 37700, - IP_gateway: '127.0.0.1' - + IP_gateway: '127.0.0.1' }; diff --git a/VAST_matcher.js b/VAST_matcher.js index fa037dd8..29ddb213 100644 --- a/VAST_matcher.js +++ b/VAST_matcher.js @@ -30,9 +30,9 @@ msg = {from_id, size, type, data, is_reliable} // message sent & received by clients sub = {sub_id, subscriber, layer, aoi, relay} // subscription info - sub_CB(result, subID) // callback to receive subscribe result (success/fail) - neighbor_CB(list) // callback to receive neighbor (node) list - recv_CB(msg) // callback to receive any messages + onSubscribed(result, subID) // callback to receive subscribe result (success/fail) + onNeighbors(list) // callback to receive neighbor (node) list + onReceive(msg) // callback to receive any messages // constructor VAST_matcher() @@ -55,14 +55,14 @@ var msg_handler = msg_handler || require('./net/msg_handler.js'); function VAST_matcher(recv_callback, settings) { // store callback to notify received messages - var _recv_CB = recv_callback; + var _onReceive = recv_callback; // // public methods // // join the matcher to a VON - this.join = function (done_CB) { + this.join = function (onDone) { // join VON network var id, port; @@ -341,7 +341,7 @@ function VAST_matcher(recv_callback, settings) { var _that = this; // function to create a new net layer - this.init = function (self_id, port, done_CB) { + this.init = function (self_id, port, onDone) { self_id = self_id || VAST.ID_UNASSIGNED; port = port || VAST_DEFAULT_PORT; @@ -353,8 +353,8 @@ function VAST_matcher(recv_callback, settings) { handler.addHandler(_that); // notify done - if (typeof done_CB === 'function') - done_CB(local_addr); + if (typeof onDone === 'function') + onDone(local_addr); }); } diff --git a/VON_peer.js b/VON_peer.js index e80f196e..cf54d4a7 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -37,18 +37,17 @@ vast_net (see definition in vast_net.js) // constructoracceptor for a given center point - join(addr, aoi, done_CB) join a VON network at a gateway with a given aoi + join(addr, aoi, onDone) join a VON network at a gateway with a given aoi leave() leave the VON network move(aoi, send_time) move the AOI to a new position (or change radius) list() get a list of AOI neighbors send(id, msg) send a message to a given node - put(obj) sto - VON_peer(aoi_buffer, aoi_use_strict) + put(obj) store app-specific data to the node // basic functions - init(id, port, done_CB) init a VON peer with id, listen port + init(id, port, onDone) init a VON peer with id, listen port shut() shutdown a VON peer (will close down listen port) - query(center, acceptor_CB) find the re a app-specific data along with the node (will pass during node discovery) + query(center, onAcceptor) find the re a app-specific data along with the node (will pass during node discovery) get() retrieve app-specific data for this node // accessors @@ -145,7 +144,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { var _that = this; // function to create a new net layer - this.init = function (self_id, port, done_CB) { + this.init = function (self_id, port, onDone) { self_id = self_id || VAST.ID_UNASSIGNED; port = port || VON_DEFAULT_PORT; @@ -157,8 +156,8 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { handler.addHandler(_that); // notify done - if (typeof done_CB === 'function') - done_CB(local_addr); + if (typeof onDone === 'function') + onDone(local_addr); }); } @@ -190,13 +189,13 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { } // join a VON network with a given aoi - var _join = this.join = function (GW_addr, aoi, done_CB) { + var _join = this.join = function (GW_addr, aoi, onDone) { // check if already joined if (_state === VAST.state.JOINED) { LOG.warn('VON_peer.join(): node already joined'); - if (typeof done_CB === 'function') - done_CB(_self.id); + if (typeof onDone === 'function') + onDone(_self.id); return; } @@ -216,7 +215,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _state = VAST.state.JOINING; // keep reference to call future once join is completed - _join_done_CB = done_CB; + _join_onDone = onDone; // if id is empty, send PING to gateway to learn of my id first // otherwise attempt to join by contacting gateway @@ -608,8 +607,8 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { _state = VAST.state.JOINED; // notify join is done - if (typeof _join_done_CB === 'function') - _join_done_CB(_self.id); + if (typeof _join_onDone === 'function') + _join_onDone(_self.id); // start ticking //_interval_id = setInterval(_tick, TICK_INTERVAL); @@ -1064,7 +1063,12 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { // forward the request if a more appropriate node exists // TODO: contains() might recompute Voronoi, isRelevantNeighbor below // also will recompute Voronoi. possible to combine into one calculation? - if (_voro.contains(_self.id, pos) === false && + if (closest === null) { + LOG.warn('closest node is null, something is not right.. please check', 'VON_QUERY'); + } + + if (closest !== null && + _voro.contains(_self.id, pos) === false && _isSelf(closest) === false && closest != from_id) { @@ -1508,10 +1512,10 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { var _self = new VAST.node(); // callback to use once join is successful - var _join_done_CB = undefined; + var _join_onDone = undefined; // callback to use once init is successful (got self ID from server) - var _init_done_CB = undefined; + var _init_onDone = undefined; // interval id for removing periodic ticking //var _interval_id = undefined; diff --git a/VSS/VSS.js b/VSS/VSS.js index 7e58ec5f..e4574807 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -1,7 +1,13 @@ -// include VAST +// include VAST (need it here?) require('../VAST'); +// default settings +global.CONFIG = { + gatewayIP: '127.0.0.1', + gatewayPort: 39900 +}; + // do not show debug //LOG.setLevel(2); @@ -9,13 +15,4 @@ var server = require("./server"); var router = require("./router"); var handlers = require("./handler"); -/* -var handle = {} -handle["publish"] = handlers.publish; -handle["subscribe"] = handlers.subscribe; -handle["unsubscribe"] = handlers.unsubscribe; -handle["query"] = handlers.query; -handle["revoke"] = handlers.revoke; -*/ - server.start(router.route, handlers); \ No newline at end of file diff --git a/VSS/directory.js b/VSS/directory.js new file mode 100644 index 00000000..6cd74f7d --- /dev/null +++ b/VSS/directory.js @@ -0,0 +1,40 @@ + +// +// directory.js VSS directory to manage node to server mapping +// +// history: +// 2013-03-15 init +// + +var _ident2addr = {}; + +// +// public calls +// + +// record a newly created node (replacing existing record under the same ident) +exports.registerNode = function (ident, addr) { + + if (_ident2addr.hasOwnProperty(ident)) + return false; + + _ident2addr[ident] = addr; + return true; +} + +// erase record for a node +exports.unregisterNode = function (ident) { + if (_ident2addr.hasOwnProperty(ident)) { + delete _ident2addr[ident]; + return true; + } + + return false; +} + +// check if a node exists and return its contact address +exports.checkNode = function (ident) { + if (_ident2addr.hasOwnProperty(ident)) + return _ident2addr[ident]; + return ''; +} diff --git a/VSS/handler.js b/VSS/handler.js index e4263254..568547de 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -3,85 +3,163 @@ // handler.js handles REST request and pass to logic to handling // -var logic = require("./logic"); +var logic = require('./logic'); // -// helpers +// directory-related // -// send back response to client -var _reply = function (res, res_obj) { +var directory = require('./directory'); - // return response if exist, otherwise response might be returned - // AFTER some callback is done handling (i.e., response will be returned within the handler) - if (typeof res_obj === 'string') { - LOG.debug('replying a string: ' + res_obj); - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.end(res_obj); - } - else { - LOG.debug('replying a JSON: ' + JSON.stringify(res_obj)); - res.writeHead(200, {'Content-Type': 'application/json'}); - res.end(JSON.stringify(res_obj)); - } +// get the VSS server address for a particular node from central directory +var _queryNodeAddress = function (ident_str, onDone) { + + LOG.warn('local address: ' + CONFIG.localAddr, 'queryNodeAddress'); + + var ident_path = ident_str.replace(/:/g, '/'); + + // parameters are: ident/addr + var url = 'http://' + CONFIG.gatewayIP + ':' + CONFIG.gatewayPort + '/manage/reg/' + ident_path + '/' + CONFIG.localAddr; + + LOG.warn('url: ' + url, 'queryNodeAddress'); + + UTIL.HTTPget(url, function (res_obj) { + + // default to self address + var addr = CONFIG.localAddr; + LOG.warn('HTTPget res: ', 'queryNodeAddress'); + LOG.warn(res_obj); + + // if ident -> addr mapping exists, + // record the mapping so we can know where to re-direct future requests + // or check if some error has occured (gateway is down) + if (res_obj === null) { + LOG.warn('error or gateway is down, store address as local', 'queryNodeAddress'); + } + else if (res_obj.addr !== '') + addr = res_obj.addr; + + var is_local = (addr === CONFIG.localAddr ? '(local)' : '(remote)'); + + LOG.warn('record [' + ident_str + '] mapping to addr: ' + addr + ' ' + is_local, 'queryNodeAddress'); + directory.registerNode(ident_str, addr); + onDone(addr); + }); } -// send back subscriber list to client -var _replyPublishPos = function (request, res) { +// check if a particular ident belongs to this VSS server +// or should be re-directed to another VSS server +var _validateIdent = exports.validateIdent = function (ident_str, onDone) { - var node = logic.getNode(request, res); - - if (node === undefined || node === null || res === undefined) { - LOG.error('node not found or response object invalid', 'replyPublishPos'); - return; + // first check if ident exists locally + var addr = directory.checkNode(ident_str); + + var handleAddress = function (addr_found) { + if (addr_found === CONFIG.localAddr) { + LOG.warn('addr found is local: ' + addr_found); + onDone(''); + } + else { + LOG.warn('addr found is remote: ' + addr_found); + onDone(addr_found); + } } - LOG.debug('replySubscribers called, node id: ' + node.getSelf().id); + // if address mapping is found, return directly + if (addr !== '') + handleAddress(addr); + else { + + // if not found then register centrally or obtain the actual VSS server + _queryNodeAddress(ident_str, handleAddress); + } +} + +// check if ident exists at this server, or forwarding is needed +// return a remote response, if forwarding is used, otherwise returns null for local action +var _checkForwarding = exports.checkForwarding = function (ident_str, pathname, onDone) { + + // check if this should be local action + _validateIdent(ident_str, function (addr_validated) { + + // should be locally executed + if (addr_validated === '') { + LOG.warn('addr found is local, locally executing: ' + pathname); + return onDone(null); + } + + // action should be executed remotely + var url = 'http://' + addr_validated + pathname; + LOG.warn('local service not available, forward to: ' + url); + + UTIL.HTTPget(url, function (response) { + + // return remote response + LOG.warn('Forwarding response received', 'checkForwarding'); + LOG.warn(response); + onDone(response); + }); + }); +} + + +// +// helpers +// + + +// send back subscriber list to client +var _replyPublishPos = function (ident) { - // list to be returned (subscribers, new neighbors, left neighbors) - var lists = logic.getLists(node); - - // return success var error = []; - _reply(res, [lists, error]); + var lists = []; + var node = logic.getNode(ident); + + if (node === undefined || node === null) { + var err_msg = 'node not found or response object invalid'; + LOG.error(err_msg, 'replyPublishPos'); + error.push(err_msg); + } + else { + // list to be returned (subscribers, new neighbors, left neighbors) + lists = logic.getLists(node); + } + + LOG.debug('node id: ' + node.getSelf().id, 'replyPublishPos'); + + // return result + return [lists, error]; } + + // // public actions // -var publish = function (words, res) { - LOG.debug("Request handler 'publish' was called."); +var publish = function (target, ident, para, onDone) { - var request = {}; - - switch (words[2]) { + switch (target) { // publishPos + // update position, or create new node if nothing exists yet case 'pos': { LOG.debug('pos ...'); - // need to update position, or create new node if nothing exists yet - + // extract parameters - request = { apikey: words[3] || '', - layer: words[4] || '', - name: words[5] || '', - x: Number(words[6]) || 0, - y: Number(words[7]) || 0, - z: Number(words[8]) || 0 + var pos = { x: Number(para[0]) || 0, + y: Number(para[1]) || 0, + z: Number(para[2]) || 0 }; - - // verify parameter validity - LOG.debug('request: ' + JSON.stringify(request)); - + // check if node's already created, if so then send update // if not then need to create a new VON peer node - var node = logic.getNode(request); + var node = logic.getNode(ident); switch (node) { // parameter invalid case undefined: { - _reply(res, ["", ["key info (apikey/layer/name) missing"]]); + onDone(["", ["key info (apikey/layer/name) missing"]]); break; } @@ -89,202 +167,234 @@ var publish = function (words, res) { case null: { // if node does not exist, create one - logic.registerNode(request, request, function (new_node) { + logic.registerNode(ident, pos, function (new_node) { LOG.debug('new_node: ' + JSON.stringify(new_node)); - _replyPublishPos(request, res); + var response = _replyPublishPos(ident); + onDone(response); }); break; } // publish pos default: { - logic.publishPos(node, request, node.getSelf().aoi.radius); - - _replyPublishPos(request, res); + logic.publishPos(node, pos, node.getSelf().aoi.radius, function () { + var response = _replyPublishPos(ident); + onDone(response); + }); break; } - } - break; - } - /* - case 'area': { - LOG.debug('area ...'); + } break; } - */ + default: { - _reply(res, JSON.stringify(request)); + onDone(); break; } - } + } } -var subscribe = function (words, res) { - LOG.debug("Request handler 'subscribe' was called."); +var subscribe = function (target, ident, para, onDone) { - var request = {}; - - switch (words[2]) { + switch (target) { // near case 'nearby': { LOG.debug('nearby ...'); // extract parameters - request = { apikey: words[3] || '', - layer: words[4] || '', - name: words[5] || '', - radius: Number(words[6]) || 0 - }; + var radius = Number(para[0]) || 0; // check parameters - if (request.radius <= 0) { + if (radius <= 0) { // return an error - _reply(res, ["", ['radius is 0 or less than 0']]); + onDone(["", ['radius is 0 or less than 0']]); break; } - + // check if node exists, return error if not yet exist (need to publishPos first) - var node = logic.getNode(request); - + var node = logic.getNode(ident); + if (node === undefined) { - _reply(res, ["", ["key info (apikey/layer/name) missing"]]); + onDone(["", ["key info (apikey/layer/name) missing"]]); } else if (node === null) { - _reply(res, ["", ['node not yet created']]); + onDone(["", ['node not yet created']]); } else { // update AOI radius for area subscription - logic.publishPos(node, node.getSelf().aoi.center, request.radius); - - // return success - _reply(res, ["OK", []]); - } + logic.publishPos(node, node.getSelf().aoi.center, radius, function () { + // return success + onDone(["OK", []]); + }); + } break; } default: { - _reply(res, JSON.stringify(request)); + onDone(); break; } } } -var unsubscribe = function (words, res) { - LOG.debug("Request handler 'unsubscribe' was called."); - - var request = {}; +var unsubscribe = function (target, ident, para, onDone) { - switch (words[2]) { + switch (target) { // near case 'nearby': { LOG.debug('nearby ...'); - // ensure this method doesn't get abused - - // extract parameters - request = { apikey: words[3] || '', - layer: words[4] || '', - name: words[5] || '' - }; + // TODO: make sure this method doesn't get abused + // check if node exists, return error if not yet exist (need to publishPos first) - var node = logic.getNode(request); + var node = logic.getNode(ident); if (node === undefined) { - _reply(res, ["", ["key info (apikey/layer/name) missing"]]); + onDone(["", ["key info (apikey/layer/name) missing"]]); } else if (node === null) { - _reply(res, ["", ['node not yet created']]); + onDone(["", ['node not yet created']]); } - else - break; - - // update AOI radius for area subscription - logic.publishPos(node, node.getSelf().aoi.center, 0); - - // return success - _reply(res, ["OK", []]); + else { + + // update AOI radius for area subscription + logic.publishPos(node, node.getSelf().aoi.center, 0, function () { + + // return success + onDone(["OK", []]); + }); + } break; } default: { - _reply(res, JSON.stringify(request)); + onDone(); break; } } } -var query = function (words, res) { - LOG.debug("Request handler 'query' was called."); - - var request = {}; +var query = function (target, ident, para, onDone) { - switch (words[2]) { + switch (target) { // get subscribers of current node case 'subscribers': { LOG.debug('subscribers ...'); - // ensure this method doesn't get abused - - // extract parameters - request = { apikey: words[3] || '', - layer: words[4] || '', - name: words[5] || '' - }; - + // TODO: ensure this method doesn't get abused (or DDoS attack) + // check if node exists, return error if not yet exist (need to publishPos first) - _replyPublishPos(request, res); - + var response = _replyPublishPos(ident); + onDone(response); break; - } + } default: { - _reply(res, JSON.stringify(request)); + onDone(); break; } } } // remove a node from system -var revoke = function (words, res) { - LOG.debug("Request handler 'revoke' was called."); - - var request = {}; +var revoke = function (target, ident, para, onDone) { - switch (words[2]) { + switch (target) { // get subscribers of current node case 'node': { LOG.debug('node ...'); - // ensure this method doesn't get abused - - // extract parameters - request = { apikey: words[3] || '', - layer: words[4] || '', - name: words[5] || '' - }; - logic.revokeNode(request, function(result) { + // ensure this method doesn't get abused + logic.revokeNode(ident, function (result) { // return success if (result === true) - _reply(res, ["OK", []]); + onDone(["OK", []]); else - _reply(res, ["", ["revoke fail"]]); + onDone(["", ["revoke fail"]]); }); break; } default: { - _reply(res, JSON.stringify(request)); + onDone(); break; } } } +// manage VAST node register / unregister / check existence +var manage = function (target, ident, para, onDone) { + + // prepare ident's string form + var ident_str = ident.apikey + ':' + ident.layer + ':' + ident.name; + + switch (target) { + + // register a new node + case 'reg': { + LOG.debug('registering new node...'); + + // extract node ident and the IP/port info of its VSS server + var addr = para[0]; + var result = directory.checkNode(ident_str); + + // if nothing was registered then store new + if (result !== '') { + // return existing + onDone({addr: result}); + break; + } + + LOG.debug('node [' + ident_str + '] register succcessful...'); + var result = directory.registerNode(ident_str, addr); + onDone({addr: ''}); + break; + } + + // unregister an existing node + case 'unreg': { + LOG.debug('unregistering node...'); + + // extract node ident and the IP/port info of its VSS server + var result = directory.unregisterNode(ident_str); + onDone({result: result}); + break; + } + + // execute remote function locally + case 'remote': { + + var func = para.func; + var args = para.args; + + var onDone = function (result) { + + var return_obj = {func: func, res: result}; + + LOG.warn('RPC returning: '); + LOG.warn(return_obj); + + onDone(return_obj); + } + + // execute the function locally + args.push(onDone); + logic[func].apply(this, args); + break; + } + + default: { + onDone(); + break; + } + } +} exports.publish = publish; exports.subscribe = subscribe; exports.unsubscribe = unsubscribe; exports.query = query; exports.revoke = revoke; - +exports.manage = manage; diff --git a/VSS/logic.js b/VSS/logic.js index c5585ff7..c7263f5a 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -21,7 +21,7 @@ var _default_radius = 0; var _nodes_created = 0; // API keys -> layers -> nodes mapping -// NOTE: it's a 3-dimensional array +// NOTE: it's a 3-dimensional map var _keys = {}; // node id -> ident mapping @@ -35,8 +35,45 @@ var _id2neighbors = {}; // public calls // -var _registerNode = exports.registerNode = function (position, info, done_CB) { +// get a specific node given its API key, layer, and name (i.e., ident) +// returns 'undefined' if key ident is missing +// returns 'null' if not found +// NOTE: the node returned is a VON_peer instance +var _getNode = exports.getNode = function (ident) { + + // check if any essential ident is missing + if (ident.apikey === '' || ident.layer === '' || ident.name === '') + return undefined; + + for (var key in _keys) + LOG.debug('key: ' + key); + + if (_keys.hasOwnProperty(ident.apikey)) { + var layers = _keys[ident.apikey]; + + for (var layer in layers) + LOG.debug('layer: ' + layer); + + if (layers.hasOwnProperty(ident.layer)) { + var nodes = layers[ident.layer]; + + for (var node in nodes) + LOG.debug('node: ' + node); + + if (nodes.hasOwnProperty(ident.name)) { + var node = nodes[ident.name]; + LOG.warn('returning a node: ' + node.getSelf().id); + return node; + } + } + } + + // no node found, warn in advance + return null; +} +var _registerNode = exports.registerNode = function (ident, position, onDone) { + var pos = new VAST.pos(position.x, position.y); // append additional z value @@ -54,15 +91,12 @@ var _registerNode = exports.registerNode = function (position, info, done_CB) { _nodes_created++; - // store node ident for ident discovery across different VSS servers - var ident = { - apikey: info.apikey, - layer: info.layer, - name: info.name - }; - - // store as meta-data - new_node.put(ident); + // store node ident for ident discovery across different VSS servers + new_node.put({ + apikey: ident.apikey, + layer: ident.layer, + name: ident.name + }); new_node.join(ip_port, aoi, @@ -72,40 +106,35 @@ var _registerNode = exports.registerNode = function (position, info, done_CB) { LOG.warn('\njoined successfully! id: ' + self_id + ' self id: ' + new_node.getSelf().id); // keep track of newly joined node in internal record - if (_keys.hasOwnProperty(info.apikey) === false) - _keys[info.apikey] = {}; - if (_keys[info.apikey].hasOwnProperty(info.layer) === false) - _keys[info.apikey][info.layer] = {}; + if (_keys.hasOwnProperty(ident.apikey) === false) + _keys[ident.apikey] = {}; + if (_keys[ident.apikey].hasOwnProperty(ident.layer) === false) + _keys[ident.apikey][ident.layer] = {}; - _keys[info.apikey][info.layer][info.name] = new_node; + _keys[ident.apikey][ident.layer][ident.name] = new_node; // store id to ident mapping - LOG.debug('store mapping for node id: ' + self_id + ' name: ' + info.name); - _id2ident[self_id] = info; - - //new_node.put(info); - + LOG.debug('store mapping for node id: ' + self_id + ' name: ' + ident.name); + _id2ident[self_id] = ident; + // check content LOG.debug('new_node (after join): ' + new_node.getSelf().toString()); - // perform initial publish pos - // (so we can get a list of neighbors) - // NOTE: doesn't appear to work... - //_publishPos(new_node, info, new_node.getSelf().aoi.radius); + // NOTE: perform initial publish pos (so we can get a list of neighbors) doesn't appear to work - done_CB(new_node); + onDone(new_node); } ); - }); + }); } -var _revokeNode = exports.revokeNode = function (info, done_CB) { +var _revokeNode = exports.revokeNode = function (ident, onDone) { // check if node exists, return error if not yet exist (need to publishPos first) - var node = _getNode(info); - + var node = getNode(ident); + if (node === undefined || node === null) - return done_CB(false); + return onDone(false); var node_id = node.getSelf().id; @@ -114,62 +143,30 @@ var _revokeNode = exports.revokeNode = function (info, done_CB) { node.shut(); node = null; - delete _keys[info.apikey][info.layer][info.name]; + delete _keys[ident.apikey][ident.layer][ident.name]; // check if we need to remove layer and/or API key - if (Object.keys(_keys[info.apikey][info.layer]).length === 0) - delete _keys[info.apikey][info.layer]; - if (Object.keys(_keys[info.apikey]).length === 0) - delete _keys[info.apikey]; + if (Object.keys(_keys[ident.apikey][ident.layer]).length === 0) + delete _keys[ident.apikey][ident.layer]; + if (Object.keys(_keys[ident.apikey]).length === 0) + delete _keys[ident.apikey]; // remove id to ident mapping - LOG.debug('remove mapping for node id: ' + node_id + ' name: ' + info.name); + LOG.debug('remove mapping for node id: ' + node_id + ' name: ' + ident.name); delete _id2ident[node_id]; - done_CB(true); + onDone(true); } -// get a specific node given its API key, layer, and name (i.e., ident) -// returns 'undefined' if key info is missing -// returns 'null' if not found -var _getNode = exports.getNode = function (ident) { +var _publishPos = exports.publishPos = function (node, pos, radius, onDone) { - // check if any essential info is missing - if (ident.apikey === '' || ident.layer === '' || ident.name === '') - return undefined; - - for (var key in _keys) - LOG.debug('key: ' + key); - - if (_keys.hasOwnProperty(ident.apikey)) { - var layers = _keys[ident.apikey]; - - for (var layer in layers) - LOG.debug('layer: ' + layer); - - if (layers.hasOwnProperty(ident.layer)) { - var nodes = layers[ident.layer]; - - for (var node in nodes) - LOG.debug('node: ' + node); - - if (nodes.hasOwnProperty(ident.name)) { - return nodes[ident.name]; - } - } - } - - // no node found, warn in advance - return null; -} - -var _publishPos = exports.publishPos = function (node, pos, radius) { - - // build new AOI info + // build new AOI ident var aoi = new VAST.area(pos, radius); // perform movement node.move(aoi); + + onDone(); } // get a list of subscribers, new neighbors, left neighbors @@ -206,44 +203,44 @@ exports.getLists = function (node) { // get node ident (from either 'meta' field or from mapping) // 'meta' means remote, mapping means on this server (?) - var info = undefined; + var ident = undefined; if (neighbor.hasOwnProperty('meta')) - info = neighbor.meta; + ident = neighbor.meta; else if (_id2ident.hasOwnProperty(id)) - info = _id2ident[id]; + ident = _id2ident[id]; // skip if // 1. is self // 2. no mapping for ident (the node is not created via VSS) if (self.id == id || - info === undefined) + ident === undefined) continue; // build unique ident for this neighbor - var ident = info.apikey + ':' + info.layer + ':' + info.name; + var ident_str = ident.apikey + ':' + ident.layer + ':' + ident.name; // check if this neighbor should be put to subscriber list // is a subscriber to myself (i.e., subscribed area covers me) if (_isSubscriber(neighbors[id], self.aoi)) - subscribe_list.push(ident); + subscribe_list.push(ident_str); // check if I am a subscriber to this neighbor if (_isSubscriber(self, neighbors[id].aoi)) { // store this neighbor to a map - curr_neighbors[id] = info; + curr_neighbors[id] = ident; // check if this neighbor is new neighbor if (prev_neighbors.hasOwnProperty(id) === false) - new_list.push(ident); + new_list.push(ident_str); } } // check if any previously known neighbor are no longer neighbors for (var nid in prev_neighbors) { if (curr_neighbors.hasOwnProperty(nid) === false) { - var info = prev_neighbors[nid]; - left_list.push(info.apikey + ':' + info.layer + ':' + info.name); + var ident = prev_neighbors[nid]; + left_list.push(ident.apikey + ':' + ident.layer + ':' + ident.name); } } @@ -268,3 +265,47 @@ var _isSubscriber = function (node, aoi) { LOG.debug('isSubscriber check if node ' + node.toString() + ' covers ' + aoi.center.toString() + ': ' + result); return result; } + + +/* +// perform same functions, except remotely +var l_remoteRPC = exports.remoteRPC = function (ident, func_name, parameters, local_func) { + + // build ident string if it's an object + if (typeof ident === 'object') + ident = (ident.apikey + ':' + ident.layer + ':' + ident.name); + + // check if this should be local action + _validateIdent(ident, function (addr_validated) { + + // should be locally executed + if (addr_validated === '') { + LOG.warn('addr found is local. locally executing: ' + func_name); + local_func(); + } + + // action should be executed remotely + else { + + LOG.warn('local service not available, call RPC for: ' + func_name + ' target: ' + addr_validated); + + // convert all arguments to array + var args = Array.prototype.slice.call(parameters); + + // assume last argument is callback + var onDone = args[args.length-1]; + LOG.warn('func_name: ' + func_name + ' onDone: ' + typeof onDone, 'remoteRPC'); + + var url = 'http://' + addr_validated + '/manage/remote/'; + UTIL.HTTPpost(url, {func: func_name, args: args.slice(0, args.length-1)}, + function (data, res) { + + LOG.warn('RPC response received', 'remoteRPC'); + LOG.warn(data); + onDone(data); + } + ); + } + }); +} +*/ diff --git a/VSS/router.js b/VSS/router.js index 3f3e1358..46db9a98 100644 --- a/VSS/router.js +++ b/VSS/router.js @@ -4,6 +4,8 @@ */ +var urlParser = require('url'); + function printWords(words) { var str = ''; @@ -13,25 +15,88 @@ function printWords(words) { return str; } -function route(handle, pathname, res) { - LOG.debug("about to route a request for " + pathname); - - // extract first verb in path - var words = pathname.split("/"); - for (var i=0; i < words.length; i++) - LOG.debug(words[i]); - - var verb = words[1]; - LOG.debug('verb is: ' + verb); - - if (typeof handle[verb] === 'function') { - handle[verb](words, res); +// send back response to client +var _reply = function (res, res_obj) { + + // return response if exist, otherwise response might be returned + // AFTER some callback is done handling (i.e., response will be returned within the handler) + if (typeof res_obj === 'string') { + LOG.debug('replying a string: ' + res_obj); + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end(res_obj); } else { - LOG.warn('no request handle for: ' + pathname); - res.writeHead(404, {'Content-Type': 'text/plain'}); - res.end('404 Not Found: ' + pathname); + LOG.debug('replying a JSON: ' + JSON.stringify(res_obj)); + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(res_obj)); } } -exports.route = route; \ No newline at end of file +function route(handlers, req, res, JSONobj) { + LOG.debug('routing a request: ' + req.url); + + // print out path + var obj = urlParser.parse(req.url, true); + var pathname = obj.pathname; + + // extract action/target/path + var words = pathname.split('/'); + var action = words[1]; + var target = words[2]; + var ident = {}; + ident.apikey = words[3] || ''; + ident.layer = words[4] || ''; + ident.name = words[5] || ''; + + var para = words.slice(6, words.length); + var ident_str = ident.apikey + ':' + ident.layer + ':' + ident.name; + + // check valid ident and actions are provided + + var local_action = function (remote_res) { + + // remote action, simply return response + if (remote_res !== null) + _reply(res, remote_res); + // check if local handlers exist + // NOTE: we can skip the above to perform single VSS server scenario + else { + LOG.debug('calling request handler for: ' + action); + + // replace 'para' content if POST data exists + if (JSONobj !== undefined) + para = JSONobj; + + handlers[action](target, ident, para, function (response) { + // if action was not handled + if (response === undefined) + _reply(res, 'unrecongizable [' + action + '] parameter: ' + target); + else + _reply(res, response); + }); + } + } + + // always perform manage requests locally + if (action === 'manage') + local_action(null); + // check if action is valid + else if (typeof handlers[action] !== 'function') { + + // ignore favicon + if (req.url === '/favicon.ico') + return res.end(); + + LOG.warn('no request handle for: ' + action); + res.writeHead(404, {'Content-Type': 'text/plain'}); + res.end('404 Not Found: ' + req.url); + } + // check if ident is valid + else if (ident.apikey === '' || ident.layer === '' || ident.name === '') { + _reply(res, 'ident info is not enough (apikey/layer/name) missing'); + } + else + handlers.checkForwarding(ident_str, pathname, local_action); +} + +exports.route = route; diff --git a/VSS/server.js b/VSS/server.js index 09490a84..ef1be575 100644 --- a/VSS/server.js +++ b/VSS/server.js @@ -9,24 +9,58 @@ */ -var serverPort = 39900; - var http = require('http'); -var urlParser = require('url'); // start server var start = function (route, handle) { - - http.createServer(function (req, res) { - // print out path - var obj = urlParser.parse(req.url, true); - - route(handle, obj.pathname, res); - - }).listen(serverPort); + // set binding port, default to be the same as gateway + var serverPort = CONFIG.gatewayPort; + + // use port argument, if available + if (process.argv[2] !== undefined) { + var addr = UTIL.parseAddress(process.argv[2]); + serverPort = addr.port; + LOG.warn('setting custom VSS server port: ' + serverPort); + } + + // get local IP + UTIL.getLocalIP(function (local_IP) { + + CONFIG.localIP = local_IP; + CONFIG.localPort = serverPort; + CONFIG.localAddr = local_IP + ':' + serverPort; + + http.createServer(function (req, res) { + + // temp buffer for incoming request + var data = ''; + + req.on('data', function (chunk) { + data += chunk; + }); + + req.on('end', function() { + + var JSONobj = undefined; + try { + if (data !== '') { + data = decodeURIComponent(data); + LOG.warn('data to parse: ' + data); + JSONobj = JSON.parse(data); + } + } + catch (e) { + LOG.error('JSON parsing error for data: ' + data); + } + + route(handle, req, res, JSONobj); + }) + + }).listen(serverPort); - LOG.warn('Server running at http://127.0.0.1:' + serverPort + '/'); + LOG.warn('Server running at http://' + local_IP + ':' + serverPort + '/'); + }); } exports.start = start; \ No newline at end of file diff --git a/common/logger.js b/common/logger.js index 7ed35ab4..4506aa0a 100644 --- a/common/logger.js +++ b/common/logger.js @@ -41,6 +41,12 @@ function logger() { _level = level; console.log('set error level to be: ' + level); } + + this.sys = function (msg) { + msg = _convert(msg); + if (_level >= 4) + console.log(_curr_time() + msg); + } this.debug = function (msg) { msg = _convert(msg); diff --git a/common/util.js b/common/util.js index c5158fdc..5a64bce6 100644 --- a/common/util.js +++ b/common/util.js @@ -3,9 +3,154 @@ common utilities for vast.js */ +const http = require('http'); +const https = require('https'); +const url = require('url'); + // returns number of milliseconds since 1970 exports.getTimestamp = function () { var now = new Date(); return now.getTime(); } +exports.parseAddress = function (ip_port) { + + var addr = {host: '', port: 0}; + + // check if this is port only + var idx = ip_port.search(':'); + + // ':' not found, port only + if (idx === (-1)) + addr.port = parseInt(ip_port); + else { + addr.host = ip_port.slice(0, idx); + addr.port = ip_port.slice(idx+1, ip_port.length); + } + + return addr; +} + +// +// support for HTTP requests (GET/POST) +// + +// helper to send HTTP post request to an URL with JSON parameters +// ref: http://stackoverflow.com/questions/6158933/http-post-request-in-node-js +// onDone = function(error, response) +var l_HTTPpost = exports.HTTPpost = function (url_request, data_obj, onDone) { + + // parse the url first to extract different fields + var parsed_url = url.parse(url_request); + + // Build the post string from an object to string format + //var data = querystring.stringify(data_obj); + var data = encodeURIComponent(JSON.stringify(data_obj)); + + //LOG.warn('POSTing to: ' + url_request + ':'); + //LOG.warn(data); + + // An object of options to indicate where to post to + var options = { + host: parsed_url.hostname, + port: parsed_url.port, + path: parsed_url.path, + method: 'POST', + headers: { + //'Connection': 'keep-alive', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': data.length + } + }; + + // setup server to HTTP or HTTPS + var server = (url_request.indexOf('https') === 0 ? https : http); + + // Set up the request + var req = server.request(options, function (res) { + + res.setEncoding('utf8'); + + // TODO: check for 'end' event? + res.on('data', function (chunk) { + LOG.sys('HTTP Post response: ' + chunk); + // return some positive data + //onDone(null, res, chunk); + onDone(chunk, res); + }); + }); + + req.on('error', function (e) { + onDone(e); + }); + + // post the data + req.write(data); + req.end(); +} + + +// helper to send a HTTP get request to an URL and get response +// TODO: this is borrowed from ImonCloud, possibly share them? +var l_HTTPget = exports.HTTPget = function (url, onDone) { + + // send request to app server to get stat + http.get(url, function (res) { + + // temp buffer for incoming request + var data = ''; + + res.on('data', function (chunk) { + data += chunk; + }); + + res.on('end', function() { + + var JSONobj = undefined; + try { + if (data !== '') + JSONobj = JSON.parse(data); + } + catch (e) { + LOG.error('JSON parsing error for data: ' + data); + onDone(null); + } + + // return parsed JSON object + onDone(JSONobj); + }) + + }).on('error', function(e) { + + LOG.error("HTTP get error: " + e.message); + onDone(null); + }); +} + + +// get & store local IP +// return the host IP for the current machine +var _localIP = undefined; +exports.getLocalIP = function (CB_done) { + + var hostname = require('os').hostname(); + LOG.sys('hostname: ' + hostname, 'getLocalIP'); + + // if already available, return directly + if (_localIP !== undefined) + return CB_done(_localIP); + + require('dns').lookup(hostname, function (err, addr, fam) { + + if (err) { + LOG.warn(err + '. Assign 127.0.0.1 to host'); + _localIP = "127.0.0.1"; + } + else + _localIP = addr; + + CB_done(_localIP); + }) +} + + diff --git a/net/vast_net.js b/net/vast_net.js index e6836a98..c150993e 100644 --- a/net/vast_net.js +++ b/net/vast_net.js @@ -571,7 +571,7 @@ function vast_net(onReceive, onConnect, onDisconnect, id) { // pass message to upper layer for handling if (typeof onReceive === 'function') { LOG.warn('[' + _self_id + '] process pack by upper layer from [' + remote_id + ']: '); - LOG.warn(pack); + //LOG.warn(pack); // TODO: queue-up message to be processed later? onReceive(remote_id, pack); } diff --git a/test/test_VON_peer.js b/test/test_VON_peer.js index 9a3e7cbf..f8897604 100644 --- a/test/test_VON_peer.js +++ b/test/test_VON_peer.js @@ -19,21 +19,15 @@ var is_client = false; // IP/port if (process.argv[2] !== undefined) { - var ip_port = process.argv[2]; - // check if this is port only - var idx = ip_port.search(':'); - - // ':' not found, port only - if (idx === (-1)) - gateway_addr.port = parseInt(ip_port); - else { - var ip = ip_port.slice(0, idx); - var port = ip_port.slice(idx+1, ip_port.length); - gateway_addr.host = ip; - gateway_addr.port = parseInt(port); - - is_client = true; - } + var addr = UTIL.parseAddress(process.argv[2]); + + // if this is IP + port + if (addr.host === '') + addr.host = VAST.Settings.IP_gateway; + else + is_client = true; + + gateway_addr = addr; } LOG.debug('GW ip: ' + gateway_addr.host + ' port: ' + gateway_addr.port); diff --git a/vast_voro.js b/vast_voro.js index d6ed14d9..f2b88d53 100644 --- a/vast_voro.js +++ b/vast_voro.js @@ -319,7 +319,7 @@ function VAST_Voronoi(bbox) { // (i.e. when two or more sites are both closest to the point, smaller ID is returned) this.closest_to = function (pos) { - // add function + // make sure position has a distance function if (typeof pos.distance !== 'function') pos = new point2d(pos.x, pos.y); From 2228abe146ea16689103c5404cc395c308753997 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Sun, 7 Apr 2013 15:52:15 +0800 Subject: [PATCH 024/141] update VSS logic --- VAST.js | 14 +- VSS/logic.js | 50 +--- common/util.js | 2 + test/test_net.js | 3 - vast.io/socket.io-client.html | 510 ++++++++++++++++++++++++++++++++++ 5 files changed, 527 insertions(+), 52 deletions(-) create mode 100644 vast.io/socket.io-client.html diff --git a/VAST.js b/VAST.js index b94dde31..fa107733 100644 --- a/VAST.js +++ b/VAST.js @@ -7,16 +7,20 @@ // utilities // -var logger = require('./common/logger'); -global.LOG = new logger(); -global.UTIL = require('./common/util'); +// define log if not defined +if (typeof global.LOG === 'undefined') { + var logger = require('./common/logger'); + global.LOG = new logger(); + + // set default error level + LOG.setLevel(3); +} -// set default error level -LOG.setLevel(3); // // VAST & VON // +global.UTIL = require('./common/util'); global.VAST = require('./vast_types'); global.VAST.net = require('./net/vast_net'); diff --git a/VSS/logic.js b/VSS/logic.js index c7263f5a..c96e0501 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -6,6 +6,8 @@ // 2013-03-12 separated from handler.js to be sharable // +// TODO: need to + // include VAST require('../VAST'); @@ -86,10 +88,11 @@ var _registerNode = exports.registerNode = function (ident, position, onDone) { var ip_port = new VAST.addr(_VON_gateway, _VON_port); var new_node = new VON.peer(); - // join in the network + // join in the network + // TODO: keep track and recycle ports? new_node.init(VAST.ID_UNASSIGNED, ip_port.port + _nodes_created, function () { - _nodes_created++; + _nodes_created++; // store node ident for ident discovery across different VSS servers new_node.put({ @@ -158,6 +161,7 @@ var _revokeNode = exports.revokeNode = function (ident, onDone) { onDone(true); } +// update position or radius (AOI) var _publishPos = exports.publishPos = function (node, pos, radius, onDone) { // build new AOI ident @@ -267,45 +271,3 @@ var _isSubscriber = function (node, aoi) { } -/* -// perform same functions, except remotely -var l_remoteRPC = exports.remoteRPC = function (ident, func_name, parameters, local_func) { - - // build ident string if it's an object - if (typeof ident === 'object') - ident = (ident.apikey + ':' + ident.layer + ':' + ident.name); - - // check if this should be local action - _validateIdent(ident, function (addr_validated) { - - // should be locally executed - if (addr_validated === '') { - LOG.warn('addr found is local. locally executing: ' + func_name); - local_func(); - } - - // action should be executed remotely - else { - - LOG.warn('local service not available, call RPC for: ' + func_name + ' target: ' + addr_validated); - - // convert all arguments to array - var args = Array.prototype.slice.call(parameters); - - // assume last argument is callback - var onDone = args[args.length-1]; - LOG.warn('func_name: ' + func_name + ' onDone: ' + typeof onDone, 'remoteRPC'); - - var url = 'http://' + addr_validated + '/manage/remote/'; - UTIL.HTTPpost(url, {func: func_name, args: args.slice(0, args.length-1)}, - function (data, res) { - - LOG.warn('RPC response received', 'remoteRPC'); - LOG.warn(data); - onDone(data); - } - ); - } - }); -} -*/ diff --git a/common/util.js b/common/util.js index 5a64bce6..597fce95 100644 --- a/common/util.js +++ b/common/util.js @@ -94,6 +94,8 @@ var l_HTTPpost = exports.HTTPpost = function (url_request, data_obj, onDone) { // TODO: this is borrowed from ImonCloud, possibly share them? var l_HTTPget = exports.HTTPget = function (url, onDone) { + LOG.warn(url); + // send request to app server to get stat http.get(url, function (res) { diff --git a/test/test_net.js b/test/test_net.js index ec1d3692..7eed397a 100644 --- a/test/test_net.js +++ b/test/test_net.js @@ -47,9 +47,6 @@ net_layer.on( console.log( "before connect attempt..." ); -// make a localhost connection -//var connID = net_layer.connect( 80, "203.84.219.114" ); - // check command-line parameter var port = 1037; var IP = "127.0.0.1"; diff --git a/vast.io/socket.io-client.html b/vast.io/socket.io-client.html new file mode 100644 index 00000000..335d7b8a --- /dev/null +++ b/vast.io/socket.io-client.html @@ -0,0 +1,510 @@ + + + + + + + + + + + + + + +

vast.io Demo

+
+ +
+mouse_x: +mouse_y: +AOI radius: +
+
+

+
+ + + + + + \ No newline at end of file From e496349dbb3183ac45f5b0ac8eb1c76cba50118f Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Mon, 15 Apr 2013 11:56:42 +0800 Subject: [PATCH 025/141] FIX: socket handling to support node 0.10.4 --- VSS/directory.js | 4 +- VSS/handler.js | 10 ++-- VSS/logic.js | 4 +- common/util.js | 1 + net/net_nodejs.js | 101 ++++++++++++++---------------------- vast.io/vast.io-client.html | 4 +- 6 files changed, 50 insertions(+), 74 deletions(-) diff --git a/VSS/directory.js b/VSS/directory.js index 6cd74f7d..35ab2c7c 100644 --- a/VSS/directory.js +++ b/VSS/directory.js @@ -13,7 +13,7 @@ var _ident2addr = {}; // // record a newly created node (replacing existing record under the same ident) -exports.registerNode = function (ident, addr) { +exports.createNode = function (ident, addr) { if (_ident2addr.hasOwnProperty(ident)) return false; @@ -23,7 +23,7 @@ exports.registerNode = function (ident, addr) { } // erase record for a node -exports.unregisterNode = function (ident) { +exports.destroyNode = function (ident) { if (_ident2addr.hasOwnProperty(ident)) { delete _ident2addr[ident]; return true; diff --git a/VSS/handler.js b/VSS/handler.js index 568547de..96c606f4 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -42,7 +42,7 @@ var _queryNodeAddress = function (ident_str, onDone) { var is_local = (addr === CONFIG.localAddr ? '(local)' : '(remote)'); LOG.warn('record [' + ident_str + '] mapping to addr: ' + addr + ' ' + is_local, 'queryNodeAddress'); - directory.registerNode(ident_str, addr); + directory.createNode(ident_str, addr); onDone(addr); }); } @@ -167,7 +167,7 @@ var publish = function (target, ident, para, onDone) { case null: { // if node does not exist, create one - logic.registerNode(ident, pos, function (new_node) { + logic.createNode(ident, pos, function (new_node) { LOG.debug('new_node: ' + JSON.stringify(new_node)); var response = _replyPublishPos(ident); @@ -307,7 +307,7 @@ var revoke = function (target, ident, para, onDone) { LOG.debug('node ...'); // ensure this method doesn't get abused - logic.revokeNode(ident, function (result) { + logic.destroyNode(ident, function (result) { // return success if (result === true) onDone(["OK", []]); @@ -348,7 +348,7 @@ var manage = function (target, ident, para, onDone) { } LOG.debug('node [' + ident_str + '] register succcessful...'); - var result = directory.registerNode(ident_str, addr); + var result = directory.createNode(ident_str, addr); onDone({addr: ''}); break; } @@ -358,7 +358,7 @@ var manage = function (target, ident, para, onDone) { LOG.debug('unregistering node...'); // extract node ident and the IP/port info of its VSS server - var result = directory.unregisterNode(ident_str); + var result = directory.destroyNode(ident_str); onDone({result: result}); break; } diff --git a/VSS/logic.js b/VSS/logic.js index c96e0501..d196dcc1 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -74,7 +74,7 @@ var _getNode = exports.getNode = function (ident) { return null; } -var _registerNode = exports.registerNode = function (ident, position, onDone) { +var _createNode = exports.createNode = function (ident, position, onDone) { var pos = new VAST.pos(position.x, position.y); @@ -131,7 +131,7 @@ var _registerNode = exports.registerNode = function (ident, position, onDone) { }); } -var _revokeNode = exports.revokeNode = function (ident, onDone) { +var _destroyNode = exports.destroyNode = function (ident, onDone) { // check if node exists, return error if not yet exist (need to publishPos first) var node = getNode(ident); diff --git a/common/util.js b/common/util.js index 597fce95..572086dd 100644 --- a/common/util.js +++ b/common/util.js @@ -81,6 +81,7 @@ var l_HTTPpost = exports.HTTPpost = function (url_request, data_obj, onDone) { }); req.on('error', function (e) { + LOG.error("HTTP post error: " + e.message); onDone(e); }); diff --git a/net/net_nodejs.js b/net/net_nodejs.js index 096b1c97..dc9945de 100644 --- a/net/net_nodejs.js +++ b/net/net_nodejs.js @@ -79,7 +79,7 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // a server object for listening to incoming connections var _server = undefined; - // a client object for making connection + // a client object for making outgoing connection var _socket = undefined; // reference @@ -93,36 +93,22 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { try { // connect to the given host & port, while providing a connection listener - _socket = l_net.createConnection(host_port.port, host_port.host); - - //setupSocket(_socket, host_port); - - // NOTE: we need to specify 'connect' event handler here, as - // if it's specified in setupSocket, it may not respond / process the - // the event in time - _socket.on('connect', function () { + _socket = l_net.createConnection(host_port.port, host_port.host, function () { - // setup connected socket - // NOTE: the handler for 'connect' inside setupSocket won't be called, - // as 'connect' event is already triggered here, so whatever task needs to be duplicated here - // TODO: more elegant approach? - setupSocket(_socket, host_port); - - LOG.debug('ooutt connect success for: ' + host_port.host + ':' + host_port.port); - // store remote address & port - //_socket.host = host_port.host; - //_socket.port = host_port.port; - - _socket.disconnect = _that.disconnect; - _socket.connected = true; - - if (typeof onConnect === 'function') - onConnect(_socket); - }); - - /* - + _socket.host = host_port.host; + _socket.port = host_port.port; + LOG.debug('out connect success for: ' + _socket.host + ':' + _socket.port); + + // setup connected socket + setupSocket(_socket); + + // replace with custom disconnect handler + // TODO: needed? or we can use the same one for both incoming & outgoing? + _socket.disconnect = _that.disconnect; + }); + + /* // if there's error on the connection, 'close' will follow _socket.on('error', function(err){ LOG.error('out connect socket error: ' + err); @@ -221,42 +207,31 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // setup a new usable socket with a socket handler //----------------------------------------- - var setupSocket = function (socket, host_port) { - - //LOG.debug('setupSocket called, onReceive: ' + typeof onReceive); - - // NOTE: this won't be triggered for out-going connections, only incoming (i.e., when a listening server calls this) - socket.on('connect', function () { + var setupSocket = function (socket) { + + //LOG.debug('connection created: ' + socket.remoteAddress + ':' + socket.remotePort); - // NOTE: remoteAddress & remotePort are available for incoming connection, but not outgoing sockets - //LOG.debug('connection created: ' + socket.remoteAddress + ':' + socket.remotePort); - - // if host_port is provided, it's an outgoing connect success - if (host_port !== undefined) { - socket.host = host_port.host; - socket.port = host_port.port; - LOG.warn('out connect success for ' + socket.host + ':' + socket.port); - } - else { - socket.host = socket.remoteAddress; - socket.port = socket.remotePort; - LOG.warn('in connect success for ' + socket.host + ':' + socket.port); - } - - socket.connected = true; - - // attach convenience function - socket.disconnect = function () { - socket.end(); - } - - // notify connection, pass the connecting socket - if (typeof onConnect === 'function') - onConnect(socket); - }); + // if host & port are not yet known, it's an incoming connection + // NOTE: remoteAddress & remotePort are available for incoming connection, but not outgoing sockets + if (typeof socket.host === 'undefined') { + socket.host = socket.remoteAddress; + socket.port = socket.remotePort; + LOG.warn('in connect success for ' + socket.host + ':' + socket.port); + } + socket.connected = true; + + // attach convenience function + socket.disconnect = function () { + socket.end(); + } + + // notify connection, pass the connecting socket + if (typeof onConnect === 'function') + onConnect(socket); + // call data callback to process data, if exists - // (this happens when setupSocket is called by a listening server for setup new socket) + // (this happens when called by a listening server to setup new socket) if (typeof onReceive === 'function') { socket.on('data', function (data) { onReceive(socket, data); @@ -318,7 +293,7 @@ function net_nodejs(onReceive, onConnect, onDisconnect, onError) { // if there's error on the connection, 'close' will follow socket.on('error', function (err){ - LOG.error('ssocket error: ' + err); + LOG.error('socket error: ' + err); }); socket.setEncoding('UTF8'); diff --git a/vast.io/vast.io-client.html b/vast.io/vast.io-client.html index 6a3154ea..59169d90 100644 --- a/vast.io/vast.io-client.html +++ b/vast.io/vast.io-client.html @@ -23,8 +23,8 @@ var dy = 5; //var addr = {host: '127.0.0.1', port: 38800}; - var addr = {host: 'api.gaiasup.com', port: 38800}; - //var addr = {host: 'dev.imonology.com', port: 38800}; + //var addr = {host: 'api.gaiasup.com', port: 38800}; + var addr = {host: 'dev.imonology.com', port: 38800}; var aoi = {center: {x: self_x, y: self_y}, radius: self_radius}; var self_id = undefined; From b3fc46d813d911a359e9bdfe6ce4673248036bc4 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Tue, 16 Apr 2013 17:18:23 +0800 Subject: [PATCH 026/141] FIX: getNode not found bug in VSS/logic.js --- VSS/logic.js | 2 +- ...-client.html => vast.io-client (dev).html} | 0 vast.io/vast.io-client (gaiasup).html | 510 ++++++++++++++++++ vast.io/vast.io-server.js | 4 +- vast.io/vast.io.js | 4 +- 5 files changed, 516 insertions(+), 4 deletions(-) rename vast.io/{vast.io-client.html => vast.io-client (dev).html} (100%) create mode 100644 vast.io/vast.io-client (gaiasup).html diff --git a/VSS/logic.js b/VSS/logic.js index d196dcc1..f705139c 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -134,7 +134,7 @@ var _createNode = exports.createNode = function (ident, position, onDone) { var _destroyNode = exports.destroyNode = function (ident, onDone) { // check if node exists, return error if not yet exist (need to publishPos first) - var node = getNode(ident); + var node = _getNode(ident); if (node === undefined || node === null) return onDone(false); diff --git a/vast.io/vast.io-client.html b/vast.io/vast.io-client (dev).html similarity index 100% rename from vast.io/vast.io-client.html rename to vast.io/vast.io-client (dev).html diff --git a/vast.io/vast.io-client (gaiasup).html b/vast.io/vast.io-client (gaiasup).html new file mode 100644 index 00000000..6a3154ea --- /dev/null +++ b/vast.io/vast.io-client (gaiasup).html @@ -0,0 +1,510 @@ + + + + + + + + + + + + + + +

vast.io Demo

+
+ +
+mouse_x: +mouse_y: +AOI radius: +
+
+

+
+ + + + + + \ No newline at end of file diff --git a/vast.io/vast.io-server.js b/vast.io/vast.io-server.js index 50932e25..48400926 100644 --- a/vast.io/vast.io-server.js +++ b/vast.io/vast.io-server.js @@ -9,7 +9,7 @@ process.on('uncaughtException', function(err) { require('../VAST'); // show only warnings -LOG.setLevel(2); +LOG.setLevel(3); /* NOTE: @@ -53,7 +53,7 @@ var nodes_created = 0; // open a server at this port var io = require('socket.io').listen(server_port); -io.set('log level', 1); +io.set('log level', 2); // warpper functions around a VAST node // NOTE: when connection establishes, it means that there's a new VAST node joined diff --git a/vast.io/vast.io.js b/vast.io/vast.io.js index 426d355e..f3459b26 100644 --- a/vast.io/vast.io.js +++ b/vast.io/vast.io.js @@ -90,7 +90,9 @@ var vast = (typeof module === 'undefined' ? {} : module.exports); // TODO: validate parameters (addr has 'host' 'port'?) // create socket - socket = io.connect('http://' + addr.host + ':' + addr.port); + var socketio_url = 'http://' + addr.host + ':' + addr.port; + console.log('url: ' + socketio_url); + socket = io.connect(socketio_url); // first connect to socket.io server (to join VON) // NOTE: addr specify the socket.io server and not VON gateway (is setup at the socket.io server) From 763665f9bbacf2ccd9d5c98f066478bb010c8193 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Wed, 17 Apr 2013 14:51:55 +0800 Subject: [PATCH 027/141] FIX: VSS query/subscribers did not return as expecred' --- VSS/directory.js | 2 +- VSS/handler.js | 31 +++++++++++++++++++++++++++---- VSS/logic.js | 45 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/VSS/directory.js b/VSS/directory.js index 35ab2c7c..28ef2b38 100644 --- a/VSS/directory.js +++ b/VSS/directory.js @@ -23,7 +23,7 @@ exports.createNode = function (ident, addr) { } // erase record for a node -exports.destroyNode = function (ident) { +exports.deleteNode = function (ident) { if (_ident2addr.hasOwnProperty(ident)) { delete _ident2addr[ident]; return true; diff --git a/VSS/handler.js b/VSS/handler.js index 96c606f4..fc0b0802 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -108,7 +108,7 @@ var _checkForwarding = exports.checkForwarding = function (ident_str, pathname, // -// send back subscriber list to client +// send back lists of in/out neighbors & subscribers to client var _replyPublishPos = function (ident) { var error = []; @@ -131,6 +131,29 @@ var _replyPublishPos = function (ident) { return [lists, error]; } +// send back list of subscribers +var _replySubscribers = function (ident) { + + var error = []; + var subscribers = []; + var node = logic.getNode(ident); + + if (node === undefined || node === null) { + var err_msg = 'node not found or response object invalid'; + LOG.error(err_msg, 'replySubscribers'); + error.push(err_msg); + } + else { + // list to be returned (subscribers, new neighbors, left neighbors) + subscribers = logic.getSubscribers(node); + } + + LOG.debug('node id: ' + node.getSelf().id, 'replySubscribers'); + + // return result + return [subscribers, error]; +} + // @@ -286,7 +309,7 @@ var query = function (target, ident, para, onDone) { // TODO: ensure this method doesn't get abused (or DDoS attack) // check if node exists, return error if not yet exist (need to publishPos first) - var response = _replyPublishPos(ident); + var response = _replySubscribers(ident); onDone(response); break; } @@ -307,7 +330,7 @@ var revoke = function (target, ident, para, onDone) { LOG.debug('node ...'); // ensure this method doesn't get abused - logic.destroyNode(ident, function (result) { + logic.deleteNode(ident, function (result) { // return success if (result === true) onDone(["OK", []]); @@ -358,7 +381,7 @@ var manage = function (target, ident, para, onDone) { LOG.debug('unregistering node...'); // extract node ident and the IP/port info of its VSS server - var result = directory.destroyNode(ident_str); + var result = directory.deleteNode(ident_str); onDone({result: result}); break; } diff --git a/VSS/logic.js b/VSS/logic.js index f705139c..20273005 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -131,7 +131,7 @@ var _createNode = exports.createNode = function (ident, position, onDone) { }); } -var _destroyNode = exports.destroyNode = function (ident, onDone) { +var _deleteNode = exports.deleteNode = function (ident, onDone) { // check if node exists, return error if not yet exist (need to publishPos first) var node = _getNode(ident); @@ -141,7 +141,7 @@ var _destroyNode = exports.destroyNode = function (ident, onDone) { var node_id = node.getSelf().id; - //destroy node + //delete node node.leave(); node.shut(); node = null; @@ -257,6 +257,47 @@ exports.getLists = function (node) { return [new_list, left_list, subscribe_list]; } +// get a list of subscribers to myself +// TODO: combine overlapped functions with getLists() +exports.getSubscribers = function (node) { + + var neighbors = node.list(); + var self = node.getSelf(); + + var subscribe_list = []; + + for (var id in neighbors) { + var neighbor = neighbors[id]; + + // get node ident (from either 'meta' field or from mapping) + // 'meta' means remote, mapping means on this server (?) + var ident = undefined; + + if (neighbor.hasOwnProperty('meta')) + ident = neighbor.meta; + else if (_id2ident.hasOwnProperty(id)) + ident = _id2ident[id]; + + // skip if + // 1. is self + // 2. no mapping for ident (the node is not created via VSS) + if (self.id == id || + ident === undefined) + continue; + + // build unique ident for this neighbor + var ident_str = ident.apikey + ':' + ident.layer + ':' + ident.name; + + // check if this neighbor should be put to subscriber list + // is a subscriber to myself (i.e., subscribed area covers me) + if (_isSubscriber(neighbors[id], self.aoi)) + subscribe_list.push(ident_str); + + } + + return subscribe_list; +} + // // helpers // From 435208973c98fdc4482d857298152af9c7a58057 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Wed, 17 Apr 2013 15:08:31 +0800 Subject: [PATCH 028/141] FIX: VSS query/subscribers returns subscriber list only --- VSS/handler.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/VSS/handler.js b/VSS/handler.js index fc0b0802..f60a4ad3 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -123,10 +123,9 @@ var _replyPublishPos = function (ident) { else { // list to be returned (subscribers, new neighbors, left neighbors) lists = logic.getLists(node); + LOG.debug('node id: ' + node.getSelf().id, 'replyPublishPos'); } - - LOG.debug('node id: ' + node.getSelf().id, 'replyPublishPos'); - + // return result return [lists, error]; } From f38d2b719ffcf220e80926725439fd61b3a62766 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 18 Apr 2013 11:41:35 +0800 Subject: [PATCH 029/141] MOD: VSS revokeNode returns subscriber list instead of just OK --- VSS/handler.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/VSS/handler.js b/VSS/handler.js index f60a4ad3..fd70887a 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -145,10 +145,9 @@ var _replySubscribers = function (ident) { else { // list to be returned (subscribers, new neighbors, left neighbors) subscribers = logic.getSubscribers(node); + LOG.debug('node id: ' + node.getSelf().id, 'replySubscribers'); } - - LOG.debug('node id: ' + node.getSelf().id, 'replySubscribers'); - + // return result return [subscribers, error]; } @@ -328,11 +327,16 @@ var revoke = function (target, ident, para, onDone) { case 'node': { LOG.debug('node ...'); + // first prepare list of subscribers as return value + var response = _replySubscribers(ident); + // ensure this method doesn't get abused logic.deleteNode(ident, function (result) { // return success - if (result === true) - onDone(["OK", []]); + if (result === true) { + //onDone(["OK", []]); + onDone(response); + } else onDone(["", ["revoke fail"]]); }); From 9dc73fa86914d6195a55873d51dbacb2e341ba79 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Fri, 3 May 2013 16:12:22 +0800 Subject: [PATCH 030/141] ADD: VSS directory listNode added for a given VSS server --- VSS/directory.js | 16 ++++++ VSS/handler.js | 63 +++++++++++++++++++-- VSS/logic.js | 139 +++++++++++++++++++++++++---------------------- vast_types.js | 1 - 4 files changed, 149 insertions(+), 70 deletions(-) diff --git a/VSS/directory.js b/VSS/directory.js index 28ef2b38..2b754aaf 100644 --- a/VSS/directory.js +++ b/VSS/directory.js @@ -38,3 +38,19 @@ exports.checkNode = function (ident) { return _ident2addr[ident]; return ''; } + +// list node ident list at a matching address +exports.listNodes = function (local_addr) { + var list = []; + + for (var ident in _ident2addr) { + + if (local_addr && local_addr !== _ident2addr[ident]) + continue; + + LOG.warn(ident + ' addr: ' + _ident2addr[ident]); + list.push(ident); + } + + return list; +} diff --git a/VSS/handler.js b/VSS/handler.js index fd70887a..8bade5f2 100644 --- a/VSS/handler.js +++ b/VSS/handler.js @@ -47,6 +47,33 @@ var _queryNodeAddress = function (ident_str, onDone) { }); } +// remove the VSS server address for a particular node from central directory +var _deleteNodeAddress = function (ident_str, onDone) { + + var ident_path = ident_str.replace(/:/g, '/'); + + // parameters are: ident/addr + var url = 'http://' + CONFIG.gatewayIP + ':' + CONFIG.gatewayPort + '/manage/unreg/' + ident_path + '/' + CONFIG.localAddr; + + LOG.warn('url: ' + url, 'deleteNodeAddress'); + + UTIL.HTTPget(url, function (res_obj) { + + LOG.warn('HTTPget res: ', 'deleteNodeAddress'); + LOG.warn(res_obj); + + var result = true; + if (res_obj === null) { + LOG.warn('error or gateway is down, delete may have failed', 'deleteNodeAddress'); + result = false; + } + + if (typeof onDone === 'function') + onDone(result); + }); +} + + // check if a particular ident belongs to this VSS server // or should be re-directed to another VSS server var _validateIdent = exports.validateIdent = function (ident_str, onDone) { @@ -125,7 +152,7 @@ var _replyPublishPos = function (ident) { lists = logic.getLists(node); LOG.debug('node id: ' + node.getSelf().id, 'replyPublishPos'); } - + // return result return [lists, error]; } @@ -152,6 +179,8 @@ var _replySubscribers = function (ident) { return [subscribers, error]; } +// return a list of nodes within subscribed area + // @@ -246,8 +275,11 @@ var subscribe = function (target, ident, para, onDone) { else { // update AOI radius for area subscription logic.publishPos(node, node.getSelf().aoi.center, radius, function () { - // return success - onDone(["OK", []]); + // return list of nodes within subscribed area + var response = logic.getNeighbors(node); + onDone([response, []]); + + //onDone(["OK", []]); }); } break; @@ -309,8 +341,9 @@ var query = function (target, ident, para, onDone) { // check if node exists, return error if not yet exist (need to publishPos first) var response = _replySubscribers(ident); onDone(response); - break; + break; } + default: { onDone(); break; @@ -334,7 +367,11 @@ var revoke = function (target, ident, para, onDone) { logic.deleteNode(ident, function (result) { // return success if (result === true) { - //onDone(["OK", []]); + var ident_str = ident.apikey + ':' + ident.layer + ':' + ident.name; + + // TODO: clear record at directory server + _deleteNodeAddress(ident_str); + onDone(response); } else @@ -389,6 +426,22 @@ var manage = function (target, ident, para, onDone) { break; } + // unregister an existing node + case 'listnodes': { + + // TODO: not really correct (but just to get the field content) + var addr = ident.apikey; + LOG.warn('list nodes at: ' + addr); + + // extract node ident and the IP/port info of its VSS server + var list = directory.listNodes(addr); + + LOG.debug('listing nodes at addr: ' + addr + ', total: ' + list.length); + + onDone(list); + break; + } + // execute remote function locally case 'remote': { diff --git a/VSS/logic.js b/VSS/logic.js index 20273005..98244e8a 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -173,11 +173,13 @@ var _publishPos = exports.publishPos = function (node, pos, radius, onDone) { onDone(); } + + // get a list of subscribers, new neighbors, left neighbors exports.getLists = function (node) { // get a list of current neighbors's id - var neighbors = node.list(); + var neighbors = _getValidNeighbors(node); var self = node.getSelf(); var new_list = []; @@ -192,59 +194,31 @@ exports.getLists = function (node) { if (_id2neighbors.hasOwnProperty(self.id) === true) prev_neighbors = _id2neighbors[self.id]; - // TODO: send only those who's AOI covers me (as true subscribers, not simply enclosing neighbors) - for (var id in neighbors) { - - LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); - - // convert node id to node ident (only for those registered here) - // NOTE: current approach can only do ident translation for nodes created via this VSS server - - var neighbor = neighbors[id]; - - //for (var i in neighbor) - // LOG.debug(i + ': ' + typeof neighbor[i]); - - // get node ident (from either 'meta' field or from mapping) - // 'meta' means remote, mapping means on this server (?) - var ident = undefined; - if (neighbor.hasOwnProperty('meta')) - ident = neighbor.meta; - else if (_id2ident.hasOwnProperty(id)) - ident = _id2ident[id]; - - // skip if - // 1. is self - // 2. no mapping for ident (the node is not created via VSS) - if (self.id == id || - ident === undefined) - continue; + for (var ident in neighbors) { - // build unique ident for this neighbor - var ident_str = ident.apikey + ':' + ident.layer + ':' + ident.name; + var neighbor = neighbors[ident]; // check if this neighbor should be put to subscriber list // is a subscriber to myself (i.e., subscribed area covers me) - if (_isSubscriber(neighbors[id], self.aoi)) - subscribe_list.push(ident_str); + if (_isSubscriber(neighbors[ident], self.aoi.center)) + subscribe_list.push(ident); // check if I am a subscriber to this neighbor - if (_isSubscriber(self, neighbors[id].aoi)) { + if (_isSubscriber(self, neighbors[ident].aoi.center)) { // store this neighbor to a map - curr_neighbors[id] = ident; + curr_neighbors[ident] = true; // check if this neighbor is new neighbor - if (prev_neighbors.hasOwnProperty(id) === false) - new_list.push(ident_str); + if (prev_neighbors.hasOwnProperty(ident) === false) + new_list.push(ident); } } // check if any previously known neighbor are no longer neighbors - for (var nid in prev_neighbors) { - if (curr_neighbors.hasOwnProperty(nid) === false) { - var ident = prev_neighbors[nid]; - left_list.push(ident.apikey + ':' + ident.layer + ':' + ident.name); + for (var left_ident in prev_neighbors) { + if (curr_neighbors.hasOwnProperty(left_ident) === false) { + left_list.push(left_ident); } } @@ -258,22 +232,76 @@ exports.getLists = function (node) { } // get a list of subscribers to myself -// TODO: combine overlapped functions with getLists() exports.getSubscribers = function (node) { - var neighbors = node.list(); + var neighbors = _getValidNeighbors(node); var self = node.getSelf(); var subscribe_list = []; + for (var ident in neighbors) { + var neighbor = neighbors[ident]; + + // check if this neighbor should be put to subscriber list + // is a subscriber to myself (i.e., subscribed area covers me) + if (_isSubscriber(neighbor, self.aoi.center)) + subscribe_list.push(ident); + } + + return subscribe_list; +} + +// get a list of neighbors to a node +exports.getNeighbors = function (node) { + var neighbors = _getValidNeighbors(node); + var self = node.getSelf(); + var list = []; + + for (var ident in neighbors) { + var neighbor = neighbors[ident]; + + // store if I am a subscriber to this neighbor + if (_isSubscriber(self, neighbor.aoi.center)) + list.push(ident); + } + + return list; +} + +// +// helpers +// + +// check if a given node is a direct area subscriber for a center point +var _isSubscriber = function (node, center) { + + // NOTE: if subscription radius is 0, then we consider it's not subscribing anything + var result = (node.aoi.radius != 0 && node.aoi.covers(center)); + LOG.debug('isSubscriber check if node ' + node.toString() + ' covers ' + center.toString() + ': ' + result); + return result; +} + +// return a map of neighbors that are VSS-generated +// indexed by ident strings +var _getValidNeighbors = function (node) { + + var neighbors = node.list(); + var self = node.getSelf(); + var list = {}; + for (var id in neighbors) { + + LOG.debug('checking neighbor id: ' + id + ' against self id: ' + self.id); + + // convert node id to node ident (only for those registered here) + // NOTE: current approach can only do ident translation for nodes created via this VSS server + var neighbor = neighbors[id]; // get node ident (from either 'meta' field or from mapping) // 'meta' means remote, mapping means on this server (?) var ident = undefined; - - if (neighbor.hasOwnProperty('meta')) + if (neighbor.hasOwnProperty('meta')) ident = neighbor.meta; else if (_id2ident.hasOwnProperty(id)) ident = _id2ident[id]; @@ -287,28 +315,11 @@ exports.getSubscribers = function (node) { // build unique ident for this neighbor var ident_str = ident.apikey + ':' + ident.layer + ':' + ident.name; - - // check if this neighbor should be put to subscriber list - // is a subscriber to myself (i.e., subscribed area covers me) - if (_isSubscriber(neighbors[id], self.aoi)) - subscribe_list.push(ident_str); - + + list[ident_str] = neighbor; } - return subscribe_list; -} - -// -// helpers -// - -// check if a given node is a direct area subscriber for a center point -var _isSubscriber = function (node, aoi) { - - // NOTE: if subscription radius is 0, then we consider it's not subscribing anything - var result = (node.aoi.radius != 0 && node.aoi.covers(aoi.center)); - LOG.debug('isSubscriber check if node ' + node.toString() + ' covers ' + aoi.center.toString() + ': ' + result); - return result; + return list; } diff --git a/vast_types.js b/vast_types.js index 3d37a730..b7f93da0 100644 --- a/vast_types.js +++ b/vast_types.js @@ -162,7 +162,6 @@ var l_addr = exports.addr = function (host, port) { // convert from a generic javascript object this.parse = function (js_obj) { - //console.log('addr parse called, obj: ' + js_obj); try { //console.log('js obj host: ' + js_obj.host + ' port: ' + js_obj.port); if (js_obj.hasOwnProperty('host')) From cde43daca168ecce6cb9348fdf57f783834694cb Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 16 May 2013 18:16:13 +0800 Subject: [PATCH 031/141] ADD: VSS/logic will return unsubscribe_list --- VON_peer.js | 8 +++---- VSS/VSS.js | 2 +- VSS/logic.js | 35 +++++++++++++++++++++++++------ vast.io/vast.io-client (dev).html | 4 ++-- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/VON_peer.js b/VON_peer.js index cf54d4a7..35adbfe2 100644 --- a/VON_peer.js +++ b/VON_peer.js @@ -36,7 +36,7 @@ pack = {type, msg, group, priority, targets} // message package during delivery vast_net (see definition in vast_net.js) - // constructoracceptor for a given center point + // VON functions join(addr, aoi, onDone) join a VON network at a gateway with a given aoi leave() leave the VON network move(aoi, send_time) move the AOI to a new position (or change radius) @@ -44,10 +44,10 @@ send(id, msg) send a message to a given node put(obj) store app-specific data to the node - // basic functions + // basic external functions init(id, port, onDone) init a VON peer with id, listen port shut() shutdown a VON peer (will close down listen port) - query(center, onAcceptor) find the re a app-specific data along with the node (will pass during node discovery) + query(center, onAcceptor) find app-specific data along with the node (will pass during node discovery) get() retrieve app-specific data for this node // accessors @@ -533,7 +533,7 @@ function VONPeer(l_aoi_buffer, l_aoi_use_strict) { //_time_drop[node.id] = 0; // update last access time of this node - node.endpt.lastAccessed = UTIL.getTimestamp (); + node.endpt.lastAccessed = UTIL.getTimestamp(); // set flag so that updated nodes' states are also sent // instead of sending only updates for newly inserted node diff --git a/VSS/VSS.js b/VSS/VSS.js index e4574807..fdf08e46 100644 --- a/VSS/VSS.js +++ b/VSS/VSS.js @@ -5,7 +5,7 @@ require('../VAST'); // default settings global.CONFIG = { gatewayIP: '127.0.0.1', - gatewayPort: 39900 + gatewayPort: 39900 }; // do not show debug diff --git a/VSS/logic.js b/VSS/logic.js index 98244e8a..5fa6624e 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -32,6 +32,9 @@ var _id2ident = {}; // node id -> neighbor list mapping var _id2neighbors = {}; +// node id -> subscriber list mapping +var _id2subscribers = {}; + // // public calls @@ -74,6 +77,8 @@ var _getNode = exports.getNode = function (ident) { return null; } +// NOTE: the 'ident' passed in should be an object with element: +// {apikey: string, layer: string, name: string} var _createNode = exports.createNode = function (ident, position, onDone) { var pos = new VAST.pos(position.x, position.y); @@ -175,7 +180,7 @@ var _publishPos = exports.publishPos = function (node, pos, radius, onDone) { -// get a list of subscribers, new neighbors, left neighbors +// get a list of new neighbors, left neighbors, subscribers, left subscribers exports.getLists = function (node) { // get a list of current neighbors's id @@ -185,6 +190,7 @@ exports.getLists = function (node) { var new_list = []; var left_list = []; var subscribe_list = []; + var unsubscribe_list = []; // list of current neighbors var curr_neighbors = {}; @@ -194,13 +200,18 @@ exports.getLists = function (node) { if (_id2neighbors.hasOwnProperty(self.id) === true) prev_neighbors = _id2neighbors[self.id]; + // get prev subscriber list if available + var prev_subscribers = {}; + if (_id2subscribers.hasOwnProperty(self.id) === true) + prev_subscribers = _id2subscribers[self.id]; + for (var ident in neighbors) { var neighbor = neighbors[ident]; // check if this neighbor should be put to subscriber list // is a subscriber to myself (i.e., subscribed area covers me) - if (_isSubscriber(neighbors[ident], self.aoi.center)) + if (_isSubscriber(neighbors[ident], self.aoi.center)) subscribe_list.push(ident); // check if I am a subscriber to this neighbor @@ -208,7 +219,7 @@ exports.getLists = function (node) { // store this neighbor to a map curr_neighbors[ident] = true; - + // check if this neighbor is new neighbor if (prev_neighbors.hasOwnProperty(ident) === false) new_list.push(ident); @@ -222,16 +233,28 @@ exports.getLists = function (node) { } } + // check any previous subscriber is no longer a subscriber + for (var left_ident in prev_subscribers) { + if (subscribers.hasOwnProperty(left_ident) === false) { + unsubscribe_list.push(left_ident); + } + } + // replace neighbor list for this node _id2neighbors[self.id] = curr_neighbors; + _id2subscribers[self.id] = subscribe_list; - LOG.warn('neighbors returned. new: ' + new_list.length + - ' left: ' + left_list.length + ' subscribe: ' + subscribe_list.length); + LOG.warn('neighbors returned. ' + + ' new: ' + new_list.length + + ' left: ' + left_list.length + + ' subscribe: ' + subscribe_list.length + + ' unsubscribe: ' + unsubscribe_list.length); - return [new_list, left_list, subscribe_list]; + return [new_list, left_list, subscribe_list, unsubscribe_list]; } // get a list of subscribers to myself +// TODO: make it more efficient (not repeat what getLists already does) exports.getSubscribers = function (node) { var neighbors = _getValidNeighbors(node); diff --git a/vast.io/vast.io-client (dev).html b/vast.io/vast.io-client (dev).html index 59169d90..0d63284c 100644 --- a/vast.io/vast.io-client (dev).html +++ b/vast.io/vast.io-client (dev).html @@ -70,13 +70,13 @@ var neighbors = data.nodes; var edges = data.edges; - console.log('got ' + neighbors.length + ' neighbors!'); + //console.log('got ' + neighbors.length + ' neighbors!'); var str = ''; for (var i=0; i < neighbors.length; i++) { str += (neighbors[i].id + ' '); } - console.log('neighbors: ' + str); + //console.log('neighbors: ' + str); //for (var i=0; i < edges.length; i++) // console.log('edge ' + i + ' (' + edges[i].a.x + ', ' + edges[i].a.y + ') (' + edges[i].b.x + ', ' + edges[i].b.y + ')'); From 8f91aa1b155bf6f0c539c9e7b281db2349b77b85 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Thu, 16 May 2013 18:38:49 +0800 Subject: [PATCH 032/141] FIX: unsubscribe_list content error --- VSS/logic.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/VSS/logic.js b/VSS/logic.js index 5fa6624e..2b906488 100644 --- a/VSS/logic.js +++ b/VSS/logic.js @@ -201,6 +201,7 @@ exports.getLists = function (node) { prev_neighbors = _id2neighbors[self.id]; // get prev subscriber list if available + var curr_subscribers = {}; var prev_subscribers = {}; if (_id2subscribers.hasOwnProperty(self.id) === true) prev_subscribers = _id2subscribers[self.id]; @@ -211,8 +212,10 @@ exports.getLists = function (node) { // check if this neighbor should be put to subscriber list // is a subscriber to myself (i.e., subscribed area covers me) - if (_isSubscriber(neighbors[ident], self.aoi.center)) + if (_isSubscriber(neighbors[ident], self.aoi.center)) { subscribe_list.push(ident); + curr_subscribers[ident] = true; + } // check if I am a subscriber to this neighbor if (_isSubscriber(self, neighbors[ident].aoi.center)) { @@ -235,14 +238,14 @@ exports.getLists = function (node) { // check any previous subscriber is no longer a subscriber for (var left_ident in prev_subscribers) { - if (subscribers.hasOwnProperty(left_ident) === false) { + if (curr_subscribers.hasOwnProperty(left_ident) === false) { unsubscribe_list.push(left_ident); } } // replace neighbor list for this node _id2neighbors[self.id] = curr_neighbors; - _id2subscribers[self.id] = subscribe_list; + _id2subscribers[self.id] = curr_subscribers; LOG.warn('neighbors returned. ' + ' new: ' + new_list.length + From 8669153a3f584390bedd78cb0d2644a41c8a8ea1 Mon Sep 17 00:00:00 2001 From: Shun-Yun Hu Date: Wed, 30 Jul 2014 10:31:44 +0800 Subject: [PATCH 033/141] 0.0.1-0: FIX: make sure tests / html can execute in tests directory --- HISTORY.txt | 4 ++++ .../VAST.js architecture.txt | 0 test/test_VON_peer.html | 22 +++++++++---------- test/test_voro.html | 10 ++++----- test/test_voro_rh (interactive).html | 12 +++++----- test/test_voro_rh (specify points).html | 2 +- test/test_voro_rh.html | 10 ++++----- test/test_voro_sf.html | 8 +++---- 8 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 HISTORY.txt rename VAST.js architecture.txt => doc/VAST.js architecture.txt (100%) diff --git a/HISTORY.txt b/HISTORY.txt new file mode 100644 index 00000000..46b9c08d --- /dev/null +++ b/HISTORY.txt @@ -0,0 +1,4 @@ + +0.0.1-0 / 2014-07-30 +==================== + * FIX: make sure tests / html can execute in tests directory diff --git a/VAST.js architecture.txt b/doc/VAST.js architecture.txt similarity index 100% rename from VAST.js architecture.txt rename to doc/VAST.js architecture.txt diff --git a/test/test_VON_peer.html b/test/test_VON_peer.html index aec24a7d..e81c5b64 100644 --- a/test/test_VON_peer.html +++ b/test/test_VON_peer.html @@ -3,12 +3,12 @@ - - - - - - + + + + + + - - - - + + + + - - - - - - - - - - - - -

VONpeer Test

-
- -

Sites generator

-
- or sites randomly - -
-mouse_x: - -mouse_y: - -AOI radius: - -
-
-Display Type: -enclosing     - boundary -
-
-

Canvas

-
- - - - \ No newline at end of file diff --git a/lib/original/test/test_VON_scale.js b/lib/original/test/test_VON_scale.js deleted file mode 100755 index a1567b07..00000000 --- a/lib/original/test/test_VON_scale.js +++ /dev/null @@ -1,118 +0,0 @@ - -/* - test for many VON_peer - - 2012.08.28 init -*/ - -// flags -require('../VAST'); -var cluster_model = require('../move_cluster'); - -// set error level -LOG.setLevel(2); - -// boundary of the test dimensions -var bound = {x: 800, y: 600}; -var tick_interval = 500; -var node_speed = 5; -var node_radius = 200; - -// set default IP/port -var gateway_addr = {host: VAST.Settings.IP_gateway, port: VAST.Settings.port_gateway}; - -// IP/port -if (process.argv[2] !== undefined) { - var ip_port = process.argv[2]; - // check if this is port only - var idx = ip_port.search(':'); - - // ':' not found, port only - if (idx === (-1)) - gateway_addr.port = parseInt(ip_port); - else { - var ip = ip_port.slice(0, idx); - var port = ip_port.slice(idx+1, ip_port.length); - gateway_addr.host = ip; - gateway_addr.port = parseInt(port); - } -} - -LOG.debug('GW ip: ' + gateway_addr.host + ' port: ' + gateway_addr.port); - - -// nodes to create -var node_size = process.argv[3] || 10; - -// initialize movement model -var movement = new cluster_model({x:0, y:0}, bound, node_size, node_speed); -movement.init(); - -// a VON node unit -var VONnode = function (num, GWaddr, radius) { - - var port = GWaddr.port + num; - - var pos = movement.getpos(num-1); - - // create GW or a connecting client; - var peer = new VON.peer(); - var aoi = new VAST.area(new VAST.pos(Math.floor(pos.x), Math.floor(pos.y)), radius); - - // perform movement - var moveNode = function () { - - var new_pos = movement.getpos(num-1); - aoi.center.x = Math.floor(new_pos.x); - aoi.center.y = Math.floor(new_pos.y); - - LOG.debug('node num: ' + num + ' moves to ' + aoi.center); - peer.move(aoi); - } - - peer.init(VAST.ID_UNASSIGNED, port, function () { - - peer.join(GWaddr, aoi, - - // done callback - function (id) { - LOG.warn('joined successfully! id: ' + id + '\n'); - - if (id !== VAST.ID_GATEWAY) - setInterval(moveNode, tick_interval); - } - ); - }); -} - -var nodes_created = 0; - -// records of all nodes created so far -var nodes = []; - -// create nodes -var createNode = function () { - - nodes_created++; - LOG.debug('creating node [' + nodes_created + ']'); - - var pos = movement.getpos(nodes_created-1); - var node = new VONnode(nodes_created, gateway_addr, node_radius); - - nodes.push(node); - - // see if we want to create more - if (nodes_created < node_size) - setTimeout(createNode, 1000); -} - -LOG.debug('creating ' + node_size + ' nodes @ host: ' + gateway_addr.host + ' port: ' + gateway_addr.port); - -// create first node -createNode(); - -// keep moving -setInterval(function () { - movement.move(); -}, tick_interval); - diff --git a/lib/original/test/test_fresh.js b/lib/original/test/test_fresh.js deleted file mode 100755 index 8c3d1285..00000000 --- a/lib/original/test/test_fresh.js +++ /dev/null @@ -1,25 +0,0 @@ -// -// Test sample for using the generic network layer -// -// -// demo for using generic_net to connect / disconnect / send / recv socket messages -// - -// Include the necessary modules. -var sys = require( "sys" ); - -var Hash = require( "./hash.js" ); -var point2d = require( "./typedef/point2d.js" ); -var segment = require( "./typedef/segment.js" ); -var line2d = require( "./typedef/line2d.js" ); - -/* -var point2d = VASTTypes.point2d; // check if necessary -var line2d = VASTTypes.line2d; - -var SFVoronoi = require( "./sf_voronoi.js" ); - -var pt1 = new point2d(100, 200); -var pt2 = new point2d(200, 200); -*/ -console.log( "before connect attempt..." ); \ No newline at end of file diff --git a/lib/original/test/test_inhert.js b/lib/original/test/test_inhert.js deleted file mode 100755 index 7e1fbe0d..00000000 --- a/lib/original/test/test_inhert.js +++ /dev/null @@ -1,55 +0,0 @@ - -/* inhertance test - -*/ - -function handler() { - this.public_str0 = 'hi world'; - var private_str = 'private world'; - - // calling this can use both public & private variables (both are accessible) - this.show_str = function () { - console.log('handler show_str: pubstr: ' + this.public_str + ' pristr: ' + private_str); - } -} - -handler.prototype.public_str = 'hello world'; - -var callback = function (onSuccess) { - - //this.public_str = 'def'; - console.log('callback called...'); - onSuccess(); -} - -handler.prototype.show2 = function () { - - var func = function () { - console.log('handler show2: pubstr: ' + handler.prototype.public_str); - } - - console.log('show2, public_str0: ' + this.public_str0); - callback(func); -} - -var von_handler = function () { - - this.von_str = 'VON message'; - var von_pri_str = 'private VON msg'; - - // print out - this.show = function () { - console.log('von_handler: pubstr: ' + this.public_str); - console.log('von_handler: von_pubstr: ' + this.von_str + ' pristr: ' + von_pri_str); - - this.show_str(); - } -} -von_handler.prototype = new handler(); - - -var vonnode = new von_handler(); -vonnode.show(); -console.log('external access of pub str: ' + vonnode.public_str); -vonnode.show_str(); -vonnode.show2(); diff --git a/lib/original/test/test_net.js b/lib/original/test/test_net.js deleted file mode 100755 index 7eed397a..00000000 --- a/lib/original/test/test_net.js +++ /dev/null @@ -1,66 +0,0 @@ -// -// Test sample for using the generic network layer -// -// -// demo for using generic_net to connect / disconnect / send / recv socket messages -// - -// Include the necessary modules. -var sys = require( "sys" ); - - // Include the Generic Network layer -var net_layer = require( "./generic_net.js" ); - -// handle 'data' event -net_layer.on( - "data", - function( from, data ){ - console.log( "data: " + data ); - } -); - -// handle 'error' event -net_layer.on( - "error", - function( errorType ){ - console.log( "connect error: " + errorType ); - } -); - -// handle 'connected' event -net_layer.on( - "connected", - function( handle ){ - console.log( handle + " has connected" ); - - console.log( "connID: " + handle + " send result: " + net_layer.send( handle, "GET /\n" ) ); - } -); - -// handle 'disconnect' event -net_layer.on( - "disconnected", - function( handle ){ - console.log( handle + " has disconnected" ); - } -); - -console.log( "before connect attempt..." ); - -// check command-line parameter -var port = 1037; -var IP = "127.0.0.1"; - -if( process.argv[2] != null ) - IP = process.argv[2]; -if( process.argv[3] != null ) - port = process.argv[3]; - -var connID = net_layer.connect( port, IP ); - - - -// keep doing something - -//net_layer.disconnect(connID); - diff --git a/lib/original/test/test_net_nodejs.js b/lib/original/test/test_net_nodejs.js deleted file mode 100755 index e3c2d0f3..00000000 --- a/lib/original/test/test_net_nodejs.js +++ /dev/null @@ -1,67 +0,0 @@ - -// unit test for net_nodejs.js - -var net_nodejs = net_nodejs || require( "./net_nodejs" ); - -var net = new net_nodejs( - - // receive callback - function (socket, msg) { - console.log('incoming: ' + msg); - - // if I'm server, then return message - if (net.isServer()) { - var return_msg = 'RETURN: ' + msg; - console.log('responding: ' + return_msg); - - net.send(return_msg, socket); - } - }, - - // connect callback - function (socket) { - - console.log(socket.host + ':' + socket.port + ' connected...'); - //console.log(socket.remoteAddress + ':' + socket.remotePort + ' connected...'); - - // test send - var msg = "GET /"; - net.send(msg); - - // test disconnect - net.disconnect(); - }, - - // disconnect callback - function (socket) { - console.log(socket.host + ':' + socket.port + ' disconnected...'); - //console.log(socket.remoteAddress + ':' + socket.remotePort + ' disconnected...'); - } -); - -for (var i=0; i < process.argv.length; i++) - console.log(i + ': ' + process.argv[i]); - -var ip_port = {host: VAST.Settings.IP_gateway, port: 37}; - -// check port input, if any -var port = process.argv[2]; -if (port !== undefined) - ip_port.port = port; - -var host = process.argv[3]; -if (host !== undefined) - ip_port.host = host; - -// check whether to run server or not -// if parameter is not given, then it's a server -if (process.argv.length <= 3) { - // test listen - net.listen(ip_port.port); -} - -else { - - // test making a connection to given ip_port - net.connect(ip_port); -} diff --git a/lib/original/test/test_voro.html b/lib/original/test/test_voro.html deleted file mode 100755 index da9606f7..00000000 --- a/lib/original/test/test_voro.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - -
-Char: Time: -
- -
- - - - - - - \ No newline at end of file diff --git a/lib/original/test/test_voro.js b/lib/original/test/test_voro.js deleted file mode 100755 index a4f07772..00000000 --- a/lib/original/test/test_voro.js +++ /dev/null @@ -1,175 +0,0 @@ - -// Include the necessary modules. -//var Hash = require( "./hash.js" ); - -/* -var point2d = require( "./typedef/point2d.js" ); -var segment = require( "./typedef/segment.js" ); -var line2d = require( "./typedef/line2d.js" ); -*/ - -var point2d = point2d || require( "./typedef/point2d.js" ); -var segment = segment || require( "./typedef/segment.js" ); -var line2d = line2d || require( "./typedef/line2d.js" ); - -var Voronoi = require( "./vast_voro.js" ); -var voro = new Voronoi(); - -var points = []; -var n = 10; -var x_dim = 1000; -var y_dim = 1000; - -// build site list & print -for (var i=0; i < n; i++) -{ - points[i] = new point2d(Math.floor(Math.random() * x_dim), Math.floor(Math.random() * y_dim)); - voro.insert(i+1, points[i]); - - console.log ((i+1) + "({x: " + points[i].x + ", y: " + points[i].y + "});"); -} - -// test generic Voronoi functions - -/* -// print edges -var edges = voro.getedges(); -for (var i=0; i < edges.length; i++) { - var p1 = edges[i].va; - var p2 = edges[i].vb; - - console.log("p1 = (" + p1.x + "," + p1.y + ") p2 = (" + p2.x + "," + p2.y + ")"); -} -*/ - -// -// unit tests -// - -var test_contains = function () { - - console.log('\ntest is_contains()'); - - // test contain() - var testpt = new point2d((x_dim/2), (y_dim/2)); - for (var i = 1; i <= n; i++) { - - var contains = voro.contains(i, testpt); - if (contains) - console.log( i + ' contains (' + testpt.x + ', ' + testpt.y + '): ' + contains); - } -} - -var test_is_boundary = function() { - - console.log('\ntest is_boundary()'); - - var testpt = new point2d((x_dim/2), (y_dim/2)); - - // test is_boundary() - // check whether is given node is boundary neighbor for a given point & radius - for (var i=1; i <= n; i++) { - - var radius = x_dim/10*8; - //console.log('check if node ' + i + ' is within ' + radius + ' of (' + testpt.x + ', ' + testpt.y + ')'); - - if (voro.is_boundary(i, testpt, radius)) - console.log('site ' + i + ' is boundary of ' + testpt.to_string() + ' within ' + radius); - } -} - -var test_is_enclosing = function () { - - console.log('\ntest is_enclosing()'); - - // test is_enclosing() - // check all nodes and find enclosing neighbors of node 1 - for (var i=1; i <=n; i++) { - - var center_node = 1; - - if (voro.is_enclosing(i, center_node) == true) - console.log('site ' + i + ' is enclosing to: ' + center_node); - } -} - -var test_get_en = function () { - - console.log('\ntest get_en()'); - - // test get_en() - var test_en_id = 1; - var test_lvl = 2; - - for (var level = 1; level <= test_lvl; level++) { - var en_list = voro.get_en(test_en_id, level); - for (var i=0; i < en_list.length; i++) - console.log('site ' + test_en_id + ' has level ' + level + ' en[' + i + ']: ' + en_list[i]); - } -} - - -// test overlaps() -// go over all regions to find ones overlapped with a circle -var test_overlaps = function () { - - var pos = new point2d(100, 100); - var radius = 500; - - console.log('\ntest overlaps() (non-accurate)'); - // go over each site - for (var id = 1; id <= n; id++) { - if (voro.overlaps(id, pos, radius)) - console.log('region ' + id + ' overlaps with ' + pos.to_string() + ' with radius: ' + radius); - } - - console.log('\ntest overlaps() (accurate)'); - // go over each site - for (var id = 1; id <= n; id++) { - if (voro.overlaps(id, pos, radius, true)) - console.log('region ' + id + ' overlaps with ' + pos.to_string() + ' with radius: ' + radius); - } -} - -// test closest_to() -var test_closest_to = function () { - - console.log('\ntest closest_to()'); - - // set test point to be the center - var testpt = new point2d(x_dim/2, y_dim/2); - - var id = voro.closest_to(testpt); - console.log('closest site to center ' + testpt.to_string() + ' is: ' + id); - - return id; -} - -// test various accessors for internal data -var test_accessors = function () { - - console.log('\ntest various accessors'); - - // print size - console.log('total size: ' + voro.size()); - - // get_bounding_box() - var bbox = voro.get_bounding_box(); - console.log('(bbox) left: ' + bbox.xl + ' right: ' + bbox.xr + ' top: ' + bbox.yt + ' bottom: ' + bbox.yb); - - // print stat - var result = voro.get_result(); - console.log('cells: ' + result.cells.length + ' edges: ' + result.edges.length + ' exec time: ' + result.execTime); - - -} - -// run unit tests -test_contains(); -test_is_boundary(); -test_is_enclosing(); -test_get_en(); -test_overlaps(); -test_closest_to(); -test_accessors(); - diff --git a/lib/original/test/test_voro_rh (interactive).html b/lib/original/test/test_voro_rh (interactive).html deleted file mode 100755 index e06e19a2..00000000 --- a/lib/original/test/test_voro_rh (interactive).html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - - - - - - - - - - - - -

Voronoi Test (Raymond Hill's)

-
- -

Sites generator

-
- or sites randomly - -
-mouse_x: - -mouse_y: - -AOI radius: - -
-
-Display Type: -enclosing     - boundary -
-
-

Canvas

-
- - - - \ No newline at end of file diff --git a/lib/original/test/test_voro_rh (specify points).html b/lib/original/test/test_voro_rh (specify points).html deleted file mode 100755 index 2bc72ab0..00000000 --- a/lib/original/test/test_voro_rh (specify points).html +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - -

Voronoi Test (Raymond Hill's)

-
- -

Sites generator

-
- or sites randomly (Warning: performance might suffer the more sites you add.) -
-
-

Canvas

-
- - - - - \ No newline at end of file diff --git a/lib/original/test/test_voro_rh.html b/lib/original/test/test_voro_rh.html deleted file mode 100755 index a62823b5..00000000 --- a/lib/original/test/test_voro_rh.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - -

Voronoi Test (Raymond Hill's)

-
- -

Sites generator

-
- or sites randomly (Warning: performance might suffer the more sites you add.) -
-
-

Canvas

-
- - - - \ No newline at end of file diff --git a/lib/original/test/test_voro_sf.html b/lib/original/test/test_voro_sf.html deleted file mode 100755 index 91400060..00000000 --- a/lib/original/test/test_voro_sf.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - -

Voronoi Test (SFVoronoi)

-
- -

Sites generator

-
- or sites randomly (Warning: performance might suffer the more sites you add.) -
-
-

Canvas

-
- - - - \ No newline at end of file diff --git a/lib/original/typedef.js b/lib/original/typedef.js deleted file mode 100755 index 098860cd..00000000 --- a/lib/original/typedef.js +++ /dev/null @@ -1,178 +0,0 @@ -/* - * VAST, a scalable peer-to-peer network for virtual environments - * Copyright (C) 2005-2011 Shun-Yun Hu (syhu@ieee.org) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - - // sugars (from Crockford's Javascript tutorial) - // http://www.crockford.com/javascript/inheritance.html - - - /* - Basic Data Types - -*/ - -// -// Following are used by Voronoi class -// - -var point2d = require( "./typedef/point2d.js" ); -var segment = require( "./typedef/segment.js" ); -var line2d = require( "./typedef/line2d.js" ); - - -/* -class rect -{ -public: - point2d vertex[4]; - line2d lines[4]; - - rect (point2d& c, uint32_t w, uint32_t h) - :center (c), width (w), height(h) - { - calculateVertex(); - } - - bool is_inside (point2d& p) - { - return vertex[1].x >= p.x && vertex[3].x <= p.x && - vertex[1].y >= p.y && vertex[3].y <= p.y; - } - - void setCenter (point2d& np) - { - center.x = np.x; - center.y = np.y; - calculateVertex(); - } - - point2d getCenter () - { - return center; - } - - uint32_t getWidth() - { - return width; - } - - uint32_t getHeight() - { - return height; - } - - void setWidth (uint32_t nw) - { - width = nw; - calculateVertex(); - } - - void setHeight (uint32_t nh) - { - height = nh; - calculateVertex(); - } - -protected: - - point2d center; - int width, height; - - void calculateVertex() - { - vertex[0].x = center.x - width/2; - vertex[0].y = center.y + height/2; - vertex[1].x = center.x + width/2; - vertex[1].y = center.y + height/2; - vertex[2].x = center.x + width/2; - vertex[2].y = center.y - height/2; - vertex[3].x = center.x - width/2; - vertex[3].y = center.y - height/2; - - lines[0].a = 0 ; lines[0].b = 1 ; lines[0].c = vertex[1].y; - lines[1].a = 1 ; lines[1].b = 0 ; lines[1].c = vertex[1].x; - lines[2].a = 0 ; lines[2].b = 1 ; lines[2].c = vertex[3].y; - lines[3].a = 1 ; lines[3].b = 0 ; lines[3].c = vertex[3].x; - } - -private: - -}; -*/ - -/* - sugar to provide inheritance - http://javascript.crockford.com/inheritance.html -*/ - - -Function.prototype.method = function (name, func) { - this.prototype[name] = func; - return this; -}; - -Function.method('inherits', function (parent) { - - var d = {}, p = (this.prototype = new parent()); - this.method('uber', function uber(name) { - if (!(name in d)) { - d[name] = 0; - } - var f, r, t = d[name], v = parent.prototype; - if (t) { - while (t) { - v = v.constructor.prototype; - t -= 1; - } - f = v[name]; - } else { - f = p[name]; - if (f == this[name]) { - f = v[name]; - } - } - d[name] += 1; - r = f.apply(this, Array.prototype.slice.apply(arguments, [1])); - d[name] -= 1; - return r; - }); - return this; -}); - -Function.method('swiss', function (parent) { - for (var i = 1; i < arguments.length; i += 1) { - var name = arguments[i]; - this.prototype[name] = parent.prototype[name]; - } - return this; -}); - -// prototypical inhertance -// http://javascript.crockford.com/prototypal.html -if (typeof Object.create !== 'function') { - Object.create = function (o) { - function F() {} - F.prototype = o; - return new F(); - }; -} -//newObject = Object.create(oldObject); - - - diff --git a/lib/original/vast.io/socket.io-client.html b/lib/original/vast.io/socket.io-client.html deleted file mode 100755 index 335d7b8a..00000000 --- a/lib/original/vast.io/socket.io-client.html +++ /dev/null @@ -1,510 +0,0 @@ - - - - - - - - - - - - - - -

vast.io Demo

-
- -
-mouse_x: -mouse_y: -AOI radius: -
-
-

-
- - - - - - \ No newline at end of file diff --git a/lib/original/vast.io/socket.io/socket.io.js b/lib/original/vast.io/socket.io/socket.io.js deleted file mode 100755 index 082e169c..00000000 --- a/lib/original/vast.io/socket.io/socket.io.js +++ /dev/null @@ -1,3862 +0,0 @@ -/*! Socket.IO.js build:0.9.9, development. Copyright(c) 2011 LearnBoost MIT Licensed */ - -var io = ('undefined' === typeof module ? {} : module.exports); -(function() { - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, global) { - - /** - * IO namespace. - * - * @namespace - */ - - var io = exports; - - /** - * Socket.IO version - * - * @api public - */ - - io.version = '0.9.9'; - - /** - * Protocol implemented. - * - * @api public - */ - - io.protocol = 1; - - /** - * Available transports, these will be populated with the available transports - * - * @api public - */ - - io.transports = []; - - /** - * Keep track of jsonp callbacks. - * - * @api private - */ - - io.j = []; - - /** - * Keep track of our io.Sockets - * - * @api private - */ - io.sockets = {}; - - - /** - * Manages connections to hosts. - * - * @param {String} uri - * @Param {Boolean} force creation of new socket (defaults to false) - * @api public - */ - - io.connect = function (host, details) { - var uri = io.util.parseUri(host) - , uuri - , socket; - - if (global && global.location) { - uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); - uri.host = uri.host || (global.document - ? global.document.domain : global.location.hostname); - uri.port = uri.port || global.location.port; - } - - uuri = io.util.uniqueUri(uri); - - var options = { - host: uri.host - , secure: 'https' == uri.protocol - , port: uri.port || ('https' == uri.protocol ? 443 : 80) - , query: uri.query || '' - }; - - io.util.merge(options, details); - - if (options['force new connection'] || !io.sockets[uuri]) { - socket = new io.Socket(options); - } - - if (!options['force new connection'] && socket) { - io.sockets[uuri] = socket; - } - - socket = socket || io.sockets[uuri]; - - // if path is different from '' or / - return socket.of(uri.path.length > 1 ? uri.path : ''); - }; - -})('object' === typeof module ? module.exports : (this.io = {}), this); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, global) { - - /** - * Utilities namespace. - * - * @namespace - */ - - var util = exports.util = {}; - - /** - * Parses an URI - * - * @author Steven Levithan (MIT license) - * @api public - */ - - var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; - - var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', - 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', - 'anchor']; - - util.parseUri = function (str) { - var m = re.exec(str || '') - , uri = {} - , i = 14; - - while (i--) { - uri[parts[i]] = m[i] || ''; - } - - return uri; - }; - - /** - * Produces a unique url that identifies a Socket.IO connection. - * - * @param {Object} uri - * @api public - */ - - util.uniqueUri = function (uri) { - var protocol = uri.protocol - , host = uri.host - , port = uri.port; - - if ('document' in global) { - host = host || document.domain; - port = port || (protocol == 'https' - && document.location.protocol !== 'https:' ? 443 : document.location.port); - } else { - host = host || 'localhost'; - - if (!port && protocol == 'https') { - port = 443; - } - } - - return (protocol || 'http') + '://' + host + ':' + (port || 80); - }; - - /** - * Mergest 2 query strings in to once unique query string - * - * @param {String} base - * @param {String} addition - * @api public - */ - - util.query = function (base, addition) { - var query = util.chunkQuery(base || '') - , components = []; - - util.merge(query, util.chunkQuery(addition || '')); - for (var part in query) { - if (query.hasOwnProperty(part)) { - components.push(part + '=' + query[part]); - } - } - - return components.length ? '?' + components.join('&') : ''; - }; - - /** - * Transforms a querystring in to an object - * - * @param {String} qs - * @api public - */ - - util.chunkQuery = function (qs) { - var query = {} - , params = qs.split('&') - , i = 0 - , l = params.length - , kv; - - for (; i < l; ++i) { - kv = params[i].split('='); - if (kv[0]) { - query[kv[0]] = kv[1]; - } - } - - return query; - }; - - /** - * Executes the given function when the page is loaded. - * - * io.util.load(function () { console.log('page loaded'); }); - * - * @param {Function} fn - * @api public - */ - - var pageLoaded = false; - - util.load = function (fn) { - if ('document' in global && document.readyState === 'complete' || pageLoaded) { - return fn(); - } - - util.on(global, 'load', fn, false); - }; - - /** - * Adds an event. - * - * @api private - */ - - util.on = function (element, event, fn, capture) { - if (element.attachEvent) { - element.attachEvent('on' + event, fn); - } else if (element.addEventListener) { - element.addEventListener(event, fn, capture); - } - }; - - /** - * Generates the correct `XMLHttpRequest` for regular and cross domain requests. - * - * @param {Boolean} [xdomain] Create a request that can be used cross domain. - * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest. - * @api private - */ - - util.request = function (xdomain) { - - if (xdomain && 'undefined' != typeof XDomainRequest) { - return new XDomainRequest(); - } - - if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { - return new XMLHttpRequest(); - } - - if (!xdomain) { - try { - return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP'); - } catch(e) { } - } - - return null; - }; - - /** - * XHR based transport constructor. - * - * @constructor - * @api public - */ - - /** - * Change the internal pageLoaded value. - */ - - if ('undefined' != typeof window) { - util.load(function () { - pageLoaded = true; - }); - } - - /** - * Defers a function to ensure a spinner is not displayed by the browser - * - * @param {Function} fn - * @api public - */ - - util.defer = function (fn) { - if (!util.ua.webkit || 'undefined' != typeof importScripts) { - return fn(); - } - - util.load(function () { - setTimeout(fn, 100); - }); - }; - - /** - * Merges two objects. - * - * @api public - */ - - util.merge = function merge (target, additional, deep, lastseen) { - var seen = lastseen || [] - , depth = typeof deep == 'undefined' ? 2 : deep - , prop; - - for (prop in additional) { - if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) { - if (typeof target[prop] !== 'object' || !depth) { - target[prop] = additional[prop]; - seen.push(additional[prop]); - } else { - util.merge(target[prop], additional[prop], depth - 1, seen); - } - } - } - - return target; - }; - - /** - * Merges prototypes from objects - * - * @api public - */ - - util.mixin = function (ctor, ctor2) { - util.merge(ctor.prototype, ctor2.prototype); - }; - - /** - * Shortcut for prototypical and static inheritance. - * - * @api private - */ - - util.inherit = function (ctor, ctor2) { - function f() {}; - f.prototype = ctor2.prototype; - ctor.prototype = new f; - }; - - /** - * Checks if the given object is an Array. - * - * io.util.isArray([]); // true - * io.util.isArray({}); // false - * - * @param Object obj - * @api public - */ - - util.isArray = Array.isArray || function (obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; - }; - - /** - * Intersects values of two arrays into a third - * - * @api public - */ - - util.intersect = function (arr, arr2) { - var ret = [] - , longest = arr.length > arr2.length ? arr : arr2 - , shortest = arr.length > arr2.length ? arr2 : arr; - - for (var i = 0, l = shortest.length; i < l; i++) { - if (~util.indexOf(longest, shortest[i])) - ret.push(shortest[i]); - } - - return ret; - } - - /** - * Array indexOf compatibility. - * - * @see bit.ly/a5Dxa2 - * @api public - */ - - util.indexOf = function (arr, o, i) { - - for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0; - i < j && arr[i] !== o; i++) {} - - return j <= i ? -1 : i; - }; - - /** - * Converts enumerables to array. - * - * @api public - */ - - util.toArray = function (enu) { - var arr = []; - - for (var i = 0, l = enu.length; i < l; i++) - arr.push(enu[i]); - - return arr; - }; - - /** - * UA / engines detection namespace. - * - * @namespace - */ - - util.ua = {}; - - /** - * Whether the UA supports CORS for XHR. - * - * @api public - */ - - util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { - try { - var a = new XMLHttpRequest(); - } catch (e) { - return false; - } - - return a.withCredentials != undefined; - })(); - - /** - * Detect webkit. - * - * @api public - */ - - util.ua.webkit = 'undefined' != typeof navigator - && /webkit/i.test(navigator.userAgent); - - /** - * Detect iPad/iPhone/iPod. - * - * @api public - */ - - util.ua.iDevice = 'undefined' != typeof navigator - && /iPad|iPhone|iPod/i.test(navigator.userAgent); - -})('undefined' != typeof io ? io : module.exports, this); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.EventEmitter = EventEmitter; - - /** - * Event emitter constructor. - * - * @api public. - */ - - function EventEmitter () {}; - - /** - * Adds a listener - * - * @api public - */ - - EventEmitter.prototype.on = function (name, fn) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = fn; - } else if (io.util.isArray(this.$events[name])) { - this.$events[name].push(fn); - } else { - this.$events[name] = [this.$events[name], fn]; - } - - return this; - }; - - EventEmitter.prototype.addListener = EventEmitter.prototype.on; - - /** - * Adds a volatile listener. - * - * @api public - */ - - EventEmitter.prototype.once = function (name, fn) { - var self = this; - - function on () { - self.removeListener(name, on); - fn.apply(this, arguments); - }; - - on.listener = fn; - this.on(name, on); - - return this; - }; - - /** - * Removes a listener. - * - * @api public - */ - - EventEmitter.prototype.removeListener = function (name, fn) { - if (this.$events && this.$events[name]) { - var list = this.$events[name]; - - if (io.util.isArray(list)) { - var pos = -1; - - for (var i = 0, l = list.length; i < l; i++) { - if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { - pos = i; - break; - } - } - - if (pos < 0) { - return this; - } - - list.splice(pos, 1); - - if (!list.length) { - delete this.$events[name]; - } - } else if (list === fn || (list.listener && list.listener === fn)) { - delete this.$events[name]; - } - } - - return this; - }; - - /** - * Removes all listeners for an event. - * - * @api public - */ - - EventEmitter.prototype.removeAllListeners = function (name) { - // TODO: enable this when node 0.5 is stable - //if (name === undefined) { - //this.$events = {}; - //return this; - //} - - if (this.$events && this.$events[name]) { - this.$events[name] = null; - } - - return this; - }; - - /** - * Gets all listeners for a certain event. - * - * @api publci - */ - - EventEmitter.prototype.listeners = function (name) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = []; - } - - if (!io.util.isArray(this.$events[name])) { - this.$events[name] = [this.$events[name]]; - } - - return this.$events[name]; - }; - - /** - * Emits an event. - * - * @api public - */ - - EventEmitter.prototype.emit = function (name) { - if (!this.$events) { - return false; - } - - var handler = this.$events[name]; - - if (!handler) { - return false; - } - - var args = Array.prototype.slice.call(arguments, 1); - - if ('function' == typeof handler) { - handler.apply(this, args); - } else if (io.util.isArray(handler)) { - var listeners = handler.slice(); - - for (var i = 0, l = listeners.length; i < l; i++) { - listeners[i].apply(this, args); - } - } else { - return false; - } - - return true; - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Based on JSON2 (http://www.JSON.org/js.html). - */ - -(function (exports, nativeJSON) { - "use strict"; - - // use native JSON if it's available - if (nativeJSON && nativeJSON.parse){ - return exports.JSON = { - parse: nativeJSON.parse - , stringify: nativeJSON.stringify - } - } - - var JSON = exports.JSON = {}; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - function date(d, key) { - return isFinite(d.valueOf()) ? - d.getUTCFullYear() + '-' + - f(d.getUTCMonth() + 1) + '-' + - f(d.getUTCDate()) + 'T' + - f(d.getUTCHours()) + ':' + - f(d.getUTCMinutes()) + ':' + - f(d.getUTCSeconds()) + 'Z' : null; - }; - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value instanceof Date) { - value = date(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 ? '[]' : gap ? - '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : - '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 ? '{}' : gap ? - '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : - '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - -// If the JSON object does not yet have a parse method, give it one. - - JSON.parse = function (text, reviver) { - // The parse method takes a text and an optional reviver function, and returns - // a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - - // The walk method is used to recursively walk the resulting structure so - // that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - - // Parsing happens in four stages. In the first stage, we replace certain - // Unicode characters with escape sequences. JavaScript handles many characters - // incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - - // In the second stage, we run the text against regular expressions that look - // for non-JSON patterns. We are especially concerned with '()' and 'new' - // because they can cause invocation, and '=' because it can cause mutation. - // But just to be safe, we want to reject all unexpected forms. - - // We split the second stage into 4 regexp operations in order to work around - // crippling inefficiencies in IE's and Safari's regexp engines. First we - // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we - // replace all simple value tokens with ']' characters. Third, we delete all - // open brackets that follow a colon or comma or that begin the text. Finally, - // we look to see that the remaining characters are only whitespace or ']' or - // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - - // In the third stage we use the eval function to compile the text into a - // JavaScript structure. The '{' operator is subject to a syntactic ambiguity - // in JavaScript: it can begin a block or an object literal. We wrap the text - // in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - - // In the optional fourth stage, we recursively walk the new structure, passing - // each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' ? - walk({'': j}, '') : j; - } - - // If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , typeof JSON !== 'undefined' ? JSON : undefined -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Parser namespace. - * - * @namespace - */ - - var parser = exports.parser = {}; - - /** - * Packet types. - */ - - var packets = parser.packets = [ - 'disconnect' - , 'connect' - , 'heartbeat' - , 'message' - , 'json' - , 'event' - , 'ack' - , 'error' - , 'noop' - ]; - - /** - * Errors reasons. - */ - - var reasons = parser.reasons = [ - 'transport not supported' - , 'client not handshaken' - , 'unauthorized' - ]; - - /** - * Errors advice. - */ - - var advice = parser.advice = [ - 'reconnect' - ]; - - /** - * Shortcuts. - */ - - var JSON = io.JSON - , indexOf = io.util.indexOf; - - /** - * Encodes a packet. - * - * @api private - */ - - parser.encodePacket = function (packet) { - var type = indexOf(packets, packet.type) - , id = packet.id || '' - , endpoint = packet.endpoint || '' - , ack = packet.ack - , data = null; - - switch (packet.type) { - case 'error': - var reason = packet.reason ? indexOf(reasons, packet.reason) : '' - , adv = packet.advice ? indexOf(advice, packet.advice) : ''; - - if (reason !== '' || adv !== '') - data = reason + (adv !== '' ? ('+' + adv) : ''); - - break; - - case 'message': - if (packet.data !== '') - data = packet.data; - break; - - case 'event': - var ev = { name: packet.name }; - - if (packet.args && packet.args.length) { - ev.args = packet.args; - } - - data = JSON.stringify(ev); - break; - - case 'json': - data = JSON.stringify(packet.data); - break; - - case 'connect': - if (packet.qs) - data = packet.qs; - break; - - case 'ack': - data = packet.ackId - + (packet.args && packet.args.length - ? '+' + JSON.stringify(packet.args) : ''); - break; - } - - // construct packet with required fragments - var encoded = [ - type - , id + (ack == 'data' ? '+' : '') - , endpoint - ]; - - // data fragment is optional - if (data !== null && data !== undefined) - encoded.push(data); - - return encoded.join(':'); - }; - - /** - * Encodes multiple messages (payload). - * - * @param {Array} messages - * @api private - */ - - parser.encodePayload = function (packets) { - var decoded = ''; - - if (packets.length == 1) - return packets[0]; - - for (var i = 0, l = packets.length; i < l; i++) { - var packet = packets[i]; - decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]; - } - - return decoded; - }; - - /** - * Decodes a packet - * - * @api private - */ - - var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; - - parser.decodePacket = function (data) { - var pieces = data.match(regexp); - - if (!pieces) return {}; - - var id = pieces[2] || '' - , data = pieces[5] || '' - , packet = { - type: packets[pieces[1]] - , endpoint: pieces[4] || '' - }; - - // whether we need to acknowledge the packet - if (id) { - packet.id = id; - if (pieces[3]) - packet.ack = 'data'; - else - packet.ack = true; - } - - // handle different packet types - switch (packet.type) { - case 'error': - var pieces = data.split('+'); - packet.reason = reasons[pieces[0]] || ''; - packet.advice = advice[pieces[1]] || ''; - break; - - case 'message': - packet.data = data || ''; - break; - - case 'event': - try { - var opts = JSON.parse(data); - packet.name = opts.name; - packet.args = opts.args; - } catch (e) { } - - packet.args = packet.args || []; - break; - - case 'json': - try { - packet.data = JSON.parse(data); - } catch (e) { } - break; - - case 'connect': - packet.qs = data || ''; - break; - - case 'ack': - var pieces = data.match(/^([0-9]+)(\+)?(.*)/); - if (pieces) { - packet.ackId = pieces[1]; - packet.args = []; - - if (pieces[3]) { - try { - packet.args = pieces[3] ? JSON.parse(pieces[3]) : []; - } catch (e) { } - } - } - break; - - case 'disconnect': - case 'heartbeat': - break; - }; - - return packet; - }; - - /** - * Decodes data payload. Detects multiple messages - * - * @return {Array} messages - * @api public - */ - - parser.decodePayload = function (data) { - // IE doesn't like data[i] for unicode chars, charAt works fine - if (data.charAt(0) == '\ufffd') { - var ret = []; - - for (var i = 1, length = ''; i < data.length; i++) { - if (data.charAt(i) == '\ufffd') { - ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length))); - i += Number(length) + 1; - length = ''; - } else { - length += data.charAt(i); - } - } - - return ret; - } else { - return [parser.decodePacket(data)]; - } - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.Transport = Transport; - - /** - * This is the transport template for all supported transport methods. - * - * @constructor - * @api public - */ - - function Transport (socket, sessid) { - this.socket = socket; - this.sessid = sessid; - }; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(Transport, io.EventEmitter); - - - /** - * Indicates whether heartbeats is enabled for this transport - * - * @api private - */ - - Transport.prototype.heartbeats = function () { - return true; - } - - /** - * Handles the response from the server. When a new response is received - * it will automatically update the timeout, decode the message and - * forwards the response to the onMessage function for further processing. - * - * @param {String} data Response from the server. - * @api private - */ - - Transport.prototype.onData = function (data) { - this.clearCloseTimeout(); - - // If the connection in currently open (or in a reopening state) reset the close - // timeout since we have just received data. This check is necessary so - // that we don't reset the timeout on an explicitly disconnected connection. - if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) { - this.setCloseTimeout(); - } - - if (data !== '') { - // todo: we should only do decodePayload for xhr transports - var msgs = io.parser.decodePayload(data); - - if (msgs && msgs.length) { - for (var i = 0, l = msgs.length; i < l; i++) { - this.onPacket(msgs[i]); - } - } - } - - return this; - }; - - /** - * Handles packets. - * - * @api private - */ - - Transport.prototype.onPacket = function (packet) { - this.socket.setHeartbeatTimeout(); - - if (packet.type == 'heartbeat') { - return this.onHeartbeat(); - } - - if (packet.type == 'connect' && packet.endpoint == '') { - this.onConnect(); - } - - if (packet.type == 'error' && packet.advice == 'reconnect') { - this.isOpen = false; - } - - this.socket.onPacket(packet); - - return this; - }; - - /** - * Sets close timeout - * - * @api private - */ - - Transport.prototype.setCloseTimeout = function () { - if (!this.closeTimeout) { - var self = this; - - this.closeTimeout = setTimeout(function () { - self.onDisconnect(); - }, this.socket.closeTimeout); - } - }; - - /** - * Called when transport disconnects. - * - * @api private - */ - - Transport.prototype.onDisconnect = function () { - if (this.isOpen) this.close(); - this.clearTimeouts(); - this.socket.onDisconnect(); - return this; - }; - - /** - * Called when transport connects - * - * @api private - */ - - Transport.prototype.onConnect = function () { - this.socket.onConnect(); - return this; - } - - /** - * Clears close timeout - * - * @api private - */ - - Transport.prototype.clearCloseTimeout = function () { - if (this.closeTimeout) { - clearTimeout(this.closeTimeout); - this.closeTimeout = null; - } - }; - - /** - * Clear timeouts - * - * @api private - */ - - Transport.prototype.clearTimeouts = function () { - this.clearCloseTimeout(); - - if (this.reopenTimeout) { - clearTimeout(this.reopenTimeout); - } - }; - - /** - * Sends a packet - * - * @param {Object} packet object. - * @api private - */ - - Transport.prototype.packet = function (packet) { - this.send(io.parser.encodePacket(packet)); - }; - - /** - * Send the received heartbeat message back to server. So the server - * knows we are still connected. - * - * @param {String} heartbeat Heartbeat response from the server. - * @api private - */ - - Transport.prototype.onHeartbeat = function (heartbeat) { - this.packet({ type: 'heartbeat' }); - }; - - /** - * Called when the transport opens. - * - * @api private - */ - - Transport.prototype.onOpen = function () { - this.isOpen = true; - this.clearCloseTimeout(); - this.socket.onOpen(); - }; - - /** - * Notifies the base when the connection with the Socket.IO server - * has been disconnected. - * - * @api private - */ - - Transport.prototype.onClose = function () { - var self = this; - - /* FIXME: reopen delay causing a infinit loop - this.reopenTimeout = setTimeout(function () { - self.open(); - }, this.socket.options['reopen delay']);*/ - - this.isOpen = false; - this.socket.onClose(); - this.onDisconnect(); - }; - - /** - * Generates a connection url based on the Socket.IO URL Protocol. - * See for more details. - * - * @returns {String} Connection url - * @api private - */ - - Transport.prototype.prepareUrl = function () { - var options = this.socket.options; - - return this.scheme() + '://' - + options.host + ':' + options.port + '/' - + options.resource + '/' + io.protocol - + '/' + this.name + '/' + this.sessid; - }; - - /** - * Checks if the transport is ready to start a connection. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - Transport.prototype.ready = function (socket, fn) { - fn.call(this); - }; -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports.Socket = Socket; - - /** - * Create a new `Socket.IO client` which can establish a persistent - * connection with a Socket.IO enabled server. - * - * @api public - */ - - function Socket (options) { - this.options = { - port: 80 - , secure: false - , document: 'document' in global ? document : false - , resource: 'socket.io' - , transports: io.transports - , 'connect timeout': 10000 - , 'try multiple transports': true - , 'reconnect': true - , 'reconnection delay': 500 - , 'reconnection limit': Infinity - , 'reopen delay': 3000 - , 'max reconnection attempts': 10 - , 'sync disconnect on unload': true - , 'auto connect': true - , 'flash policy port': 10843 - , 'manualFlush': false - }; - - io.util.merge(this.options, options); - - this.connected = false; - this.open = false; - this.connecting = false; - this.reconnecting = false; - this.namespaces = {}; - this.buffer = []; - this.doBuffer = false; - - if (this.options['sync disconnect on unload'] && - (!this.isXDomain() || io.util.ua.hasCORS)) { - var self = this; - io.util.on(global, 'beforeunload', function () { - self.disconnectSync(); - }, false); - } - - if (this.options['auto connect']) { - this.connect(); - } -}; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(Socket, io.EventEmitter); - - /** - * Returns a namespace listener/emitter for this socket - * - * @api public - */ - - Socket.prototype.of = function (name) { - if (!this.namespaces[name]) { - this.namespaces[name] = new io.SocketNamespace(this, name); - - if (name !== '') { - this.namespaces[name].packet({ type: 'connect' }); - } - } - - return this.namespaces[name]; - }; - - /** - * Emits the given event to the Socket and all namespaces - * - * @api private - */ - - Socket.prototype.publish = function () { - this.emit.apply(this, arguments); - - var nsp; - - for (var i in this.namespaces) { - if (this.namespaces.hasOwnProperty(i)) { - nsp = this.of(i); - nsp.$emit.apply(nsp, arguments); - } - } - }; - - /** - * Performs the handshake - * - * @api private - */ - - function empty () { }; - - Socket.prototype.handshake = function (fn) { - var self = this - , options = this.options; - - function complete (data) { - if (data instanceof Error) { - self.connecting = false; - self.onError(data.message); - } else { - fn.apply(null, data.split(':')); - } - }; - - var url = [ - 'http' + (options.secure ? 's' : '') + ':/' - , options.host + ':' + options.port - , options.resource - , io.protocol - , io.util.query(this.options.query, 't=' + +new Date) - ].join('/'); - - if (this.isXDomain() && !io.util.ua.hasCORS) { - var insertAt = document.getElementsByTagName('script')[0] - , script = document.createElement('script'); - - script.src = url + '&jsonp=' + io.j.length; - insertAt.parentNode.insertBefore(script, insertAt); - - io.j.push(function (data) { - complete(data); - script.parentNode.removeChild(script); - }); - } else { - var xhr = io.util.request(); - - xhr.open('GET', url, true); - xhr.withCredentials = true; - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - xhr.onreadystatechange = empty; - - if (xhr.status == 200) { - complete(xhr.responseText); - } else if (xhr.status == 403) { - self.onError(xhr.responseText); - } else { - self.connecting = false; - !self.reconnecting && self.onError(xhr.responseText); - } - } - }; - xhr.send(null); - } - }; - - /** - * Find an available transport based on the options supplied in the constructor. - * - * @api private - */ - - Socket.prototype.getTransport = function (override) { - var transports = override || this.transports, match; - - for (var i = 0, transport; transport = transports[i]; i++) { - if (io.Transport[transport] - && io.Transport[transport].check(this) - && (!this.isXDomain() || io.Transport[transport].xdomainCheck(this))) { - return new io.Transport[transport](this, this.sessionid); - } - } - - return null; - }; - - /** - * Connects to the server. - * - * @param {Function} [fn] Callback. - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.connect = function (fn) { - if (this.connecting) { - return this; - } - - var self = this; - self.connecting = true; - - this.handshake(function (sid, heartbeat, close, transports) { - self.sessionid = sid; - self.closeTimeout = close * 1000; - self.heartbeatTimeout = heartbeat * 1000; - if(!self.transports) - self.transports = self.origTransports = (transports ? io.util.intersect( - transports.split(',') - , self.options.transports - ) : self.options.transports); - - self.setHeartbeatTimeout(); - - function connect (transports){ - if (self.transport) self.transport.clearTimeouts(); - - self.transport = self.getTransport(transports); - if (!self.transport) return self.publish('connect_failed'); - - // once the transport is ready - self.transport.ready(self, function () { - self.connecting = true; - self.publish('connecting', self.transport.name); - self.transport.open(); - - if (self.options['connect timeout']) { - self.connectTimeoutTimer = setTimeout(function () { - if (!self.connected) { - self.connecting = false; - - if (self.options['try multiple transports']) { - var remaining = self.transports; - - while (remaining.length > 0 && remaining.splice(0,1)[0] != - self.transport.name) {} - - if (remaining.length){ - connect(remaining); - } else { - self.publish('connect_failed'); - } - } - } - }, self.options['connect timeout']); - } - }); - } - - connect(self.transports); - - self.once('connect', function (){ - clearTimeout(self.connectTimeoutTimer); - - fn && typeof fn == 'function' && fn(); - }); - }); - - return this; - }; - - /** - * Clears and sets a new heartbeat timeout using the value given by the - * server during the handshake. - * - * @api private - */ - - Socket.prototype.setHeartbeatTimeout = function () { - clearTimeout(this.heartbeatTimeoutTimer); - if(this.transport && !this.transport.heartbeats()) return; - - var self = this; - this.heartbeatTimeoutTimer = setTimeout(function () { - self.transport.onClose(); - }, this.heartbeatTimeout); - }; - - /** - * Sends a message. - * - * @param {Object} data packet. - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.packet = function (data) { - if (this.connected && !this.doBuffer) { - this.transport.packet(data); - } else { - this.buffer.push(data); - } - - return this; - }; - - /** - * Sets buffer state - * - * @api private - */ - - Socket.prototype.setBuffer = function (v) { - this.doBuffer = v; - - if (!v && this.connected && this.buffer.length) { - if (!this.options['manualFlush']) { - this.flushBuffer(); - } - } - }; - - /** - * Flushes the buffer data over the wire. - * To be invoked manually when 'manualFlush' is set to true. - * - * @api public - */ - - Socket.prototype.flushBuffer = function() { - this.transport.payload(this.buffer); - this.buffer = []; - }; - - - /** - * Disconnect the established connect. - * - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.disconnect = function () { - if (this.connected || this.connecting) { - if (this.open) { - this.of('').packet({ type: 'disconnect' }); - } - - // handle disconnection immediately - this.onDisconnect('booted'); - } - - return this; - }; - - /** - * Disconnects the socket with a sync XHR. - * - * @api private - */ - - Socket.prototype.disconnectSync = function () { - // ensure disconnection - var xhr = io.util.request(); - var uri = [ - 'http' + (this.options.secure ? 's' : '') + ':/' - , this.options.host + ':' + this.options.port - , this.options.resource - , io.protocol - , '' - , this.sessionid - ].join('/') + '/?disconnect=1'; - - xhr.open('GET', uri, false); - xhr.send(null); - - // handle disconnection immediately - this.onDisconnect('booted'); - }; - - /** - * Check if we need to use cross domain enabled transports. Cross domain would - * be a different port or different domain name. - * - * @returns {Boolean} - * @api private - */ - - Socket.prototype.isXDomain = function () { - - var port = global.location.port || - ('https:' == global.location.protocol ? 443 : 80); - - return this.options.host !== global.location.hostname - || this.options.port != port; - }; - - /** - * Called upon handshake. - * - * @api private - */ - - Socket.prototype.onConnect = function () { - if (!this.connected) { - this.connected = true; - this.connecting = false; - if (!this.doBuffer) { - // make sure to flush the buffer - this.setBuffer(false); - } - this.emit('connect'); - } - }; - - /** - * Called when the transport opens - * - * @api private - */ - - Socket.prototype.onOpen = function () { - this.open = true; - }; - - /** - * Called when the transport closes. - * - * @api private - */ - - Socket.prototype.onClose = function () { - this.open = false; - clearTimeout(this.heartbeatTimeoutTimer); - }; - - /** - * Called when the transport first opens a connection - * - * @param text - */ - - Socket.prototype.onPacket = function (packet) { - this.of(packet.endpoint).onPacket(packet); - }; - - /** - * Handles an error. - * - * @api private - */ - - Socket.prototype.onError = function (err) { - if (err && err.advice) { - if (err.advice === 'reconnect' && (this.connected || this.connecting)) { - this.disconnect(); - if (this.options.reconnect) { - this.reconnect(); - } - } - } - - this.publish('error', err && err.reason ? err.reason : err); - }; - - /** - * Called when the transport disconnects. - * - * @api private - */ - - Socket.prototype.onDisconnect = function (reason) { - var wasConnected = this.connected - , wasConnecting = this.connecting; - - this.connected = false; - this.connecting = false; - this.open = false; - - if (wasConnected || wasConnecting) { - this.transport.close(); - this.transport.clearTimeouts(); - if (wasConnected) { - this.publish('disconnect', reason); - - if ('booted' != reason && this.options.reconnect && !this.reconnecting) { - this.reconnect(); - } - } - } - }; - - /** - * Called upon reconnection. - * - * @api private - */ - - Socket.prototype.reconnect = function () { - this.reconnecting = true; - this.reconnectionAttempts = 0; - this.reconnectionDelay = this.options['reconnection delay']; - - var self = this - , maxAttempts = this.options['max reconnection attempts'] - , tryMultiple = this.options['try multiple transports'] - , limit = this.options['reconnection limit']; - - function reset () { - if (self.connected) { - for (var i in self.namespaces) { - if (self.namespaces.hasOwnProperty(i) && '' !== i) { - self.namespaces[i].packet({ type: 'connect' }); - } - } - self.publish('reconnect', self.transport.name, self.reconnectionAttempts); - } - - clearTimeout(self.reconnectionTimer); - - self.removeListener('connect_failed', maybeReconnect); - self.removeListener('connect', maybeReconnect); - - self.reconnecting = false; - - delete self.reconnectionAttempts; - delete self.reconnectionDelay; - delete self.reconnectionTimer; - delete self.redoTransports; - - self.options['try multiple transports'] = tryMultiple; - }; - - function maybeReconnect () { - if (!self.reconnecting) { - return; - } - - if (self.connected) { - return reset(); - }; - - if (self.connecting && self.reconnecting) { - return self.reconnectionTimer = setTimeout(maybeReconnect, 1000); - } - - if (self.reconnectionAttempts++ >= maxAttempts) { - if (!self.redoTransports) { - self.on('connect_failed', maybeReconnect); - self.options['try multiple transports'] = true; - self.transports = self.origTransports; - self.transport = self.getTransport(); - self.redoTransports = true; - self.connect(); - } else { - self.publish('reconnect_failed'); - reset(); - } - } else { - if (self.reconnectionDelay < limit) { - self.reconnectionDelay *= 2; // exponential back off - } - - self.connect(); - self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts); - self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay); - } - }; - - this.options['try multiple transports'] = false; - this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay); - - this.on('connect', maybeReconnect); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.SocketNamespace = SocketNamespace; - - /** - * Socket namespace constructor. - * - * @constructor - * @api public - */ - - function SocketNamespace (socket, name) { - this.socket = socket; - this.name = name || ''; - this.flags = {}; - this.json = new Flag(this, 'json'); - this.ackPackets = 0; - this.acks = {}; - }; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(SocketNamespace, io.EventEmitter); - - /** - * Copies emit since we override it - * - * @api private - */ - - SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit; - - /** - * Creates a new namespace, by proxying the request to the socket. This - * allows us to use the synax as we do on the server. - * - * @api public - */ - - SocketNamespace.prototype.of = function () { - return this.socket.of.apply(this.socket, arguments); - }; - - /** - * Sends a packet. - * - * @api private - */ - - SocketNamespace.prototype.packet = function (packet) { - packet.endpoint = this.name; - this.socket.packet(packet); - this.flags = {}; - return this; - }; - - /** - * Sends a message - * - * @api public - */ - - SocketNamespace.prototype.send = function (data, fn) { - var packet = { - type: this.flags.json ? 'json' : 'message' - , data: data - }; - - if ('function' == typeof fn) { - packet.id = ++this.ackPackets; - packet.ack = true; - this.acks[packet.id] = fn; - } - - return this.packet(packet); - }; - - /** - * Emits an event - * - * @api public - */ - - SocketNamespace.prototype.emit = function (name) { - var args = Array.prototype.slice.call(arguments, 1) - , lastArg = args[args.length - 1] - , packet = { - type: 'event' - , name: name - }; - - if ('function' == typeof lastArg) { - packet.id = ++this.ackPackets; - packet.ack = 'data'; - this.acks[packet.id] = lastArg; - args = args.slice(0, args.length - 1); - } - - packet.args = args; - - return this.packet(packet); - }; - - /** - * Disconnects the namespace - * - * @api private - */ - - SocketNamespace.prototype.disconnect = function () { - if (this.name === '') { - this.socket.disconnect(); - } else { - this.packet({ type: 'disconnect' }); - this.$emit('disconnect'); - } - - return this; - }; - - /** - * Handles a packet - * - * @api private - */ - - SocketNamespace.prototype.onPacket = function (packet) { - var self = this; - - function ack () { - self.packet({ - type: 'ack' - , args: io.util.toArray(arguments) - , ackId: packet.id - }); - }; - - switch (packet.type) { - case 'connect': - this.$emit('connect'); - break; - - case 'disconnect': - if (this.name === '') { - this.socket.onDisconnect(packet.reason || 'booted'); - } else { - this.$emit('disconnect', packet.reason); - } - break; - - case 'message': - case 'json': - var params = ['message', packet.data]; - - if (packet.ack == 'data') { - params.push(ack); - } else if (packet.ack) { - this.packet({ type: 'ack', ackId: packet.id }); - } - - this.$emit.apply(this, params); - break; - - case 'event': - var params = [packet.name].concat(packet.args); - - if (packet.ack == 'data') - params.push(ack); - - this.$emit.apply(this, params); - break; - - case 'ack': - if (this.acks[packet.ackId]) { - this.acks[packet.ackId].apply(this, packet.args); - delete this.acks[packet.ackId]; - } - break; - - case 'error': - if (packet.advice){ - this.socket.onError(packet); - } else { - if (packet.reason == 'unauthorized') { - this.$emit('connect_failed', packet.reason); - } else { - this.$emit('error', packet.reason); - } - } - break; - } - }; - - /** - * Flag interface. - * - * @api private - */ - - function Flag (nsp, name) { - this.namespace = nsp; - this.name = name; - }; - - /** - * Send a message - * - * @api public - */ - - Flag.prototype.send = function () { - this.namespace.flags[this.name] = true; - this.namespace.send.apply(this.namespace, arguments); - }; - - /** - * Emit an event - * - * @api public - */ - - Flag.prototype.emit = function () { - this.namespace.flags[this.name] = true; - this.namespace.emit.apply(this.namespace, arguments); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports.websocket = WS; - - /** - * The WebSocket transport uses the HTML5 WebSocket API to establish an - * persistent connection with the Socket.IO server. This transport will also - * be inherited by the FlashSocket fallback as it provides a API compatible - * polyfill for the WebSockets. - * - * @constructor - * @extends {io.Transport} - * @api public - */ - - function WS (socket) { - io.Transport.apply(this, arguments); - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(WS, io.Transport); - - /** - * Transport name - * - * @api public - */ - - WS.prototype.name = 'websocket'; - - /** - * Initializes a new `WebSocket` connection with the Socket.IO server. We attach - * all the appropriate listeners to handle the responses from the server. - * - * @returns {Transport} - * @api public - */ - - WS.prototype.open = function () { - var query = io.util.query(this.socket.options.query) - , self = this - , Socket - - - if (!Socket) { - Socket = global.MozWebSocket || global.WebSocket; - } - - this.websocket = new Socket(this.prepareUrl() + query); - - this.websocket.onopen = function () { - self.onOpen(); - self.socket.setBuffer(false); - }; - this.websocket.onmessage = function (ev) { - self.onData(ev.data); - }; - this.websocket.onclose = function () { - self.onClose(); - self.socket.setBuffer(true); - }; - this.websocket.onerror = function (e) { - self.onError(e); - }; - - return this; - }; - - /** - * Send a message to the Socket.IO server. The message will automatically be - * encoded in the correct message format. - * - * @returns {Transport} - * @api public - */ - - // Do to a bug in the current IDevices browser, we need to wrap the send in a - // setTimeout, when they resume from sleeping the browser will crash if - // we don't allow the browser time to detect the socket has been closed - if (io.util.ua.iDevice) { - WS.prototype.send = function (data) { - var self = this; - setTimeout(function() { - self.websocket.send(data); - },0); - return this; - }; - } else { - WS.prototype.send = function (data) { - this.websocket.send(data); - return this; - }; - } - - /** - * Payload - * - * @api private - */ - - WS.prototype.payload = function (arr) { - for (var i = 0, l = arr.length; i < l; i++) { - this.packet(arr[i]); - } - return this; - }; - - /** - * Disconnect the established `WebSocket` connection. - * - * @returns {Transport} - * @api public - */ - - WS.prototype.close = function () { - this.websocket.close(); - return this; - }; - - /** - * Handle the errors that `WebSocket` might be giving when we - * are attempting to connect or send messages. - * - * @param {Error} e The error. - * @api private - */ - - WS.prototype.onError = function (e) { - this.socket.onError(e); - }; - - /** - * Returns the appropriate scheme for the URI generation. - * - * @api private - */ - WS.prototype.scheme = function () { - return this.socket.options.secure ? 'wss' : 'ws'; - }; - - /** - * Checks if the browser has support for native `WebSockets` and that - * it's not the polyfill created for the FlashSocket transport. - * - * @return {Boolean} - * @api public - */ - - WS.check = function () { - return ('WebSocket' in global && !('__addTask' in WebSocket)) - || 'MozWebSocket' in global; - }; - - /** - * Check if the `WebSocket` transport support cross domain communications. - * - * @returns {Boolean} - * @api public - */ - - WS.xdomainCheck = function () { - return true; - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('websocket'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.flashsocket = Flashsocket; - - /** - * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket - * specification. It uses a .swf file to communicate with the server. If you want - * to serve the .swf file from a other server than where the Socket.IO script is - * coming from you need to use the insecure version of the .swf. More information - * about this can be found on the github page. - * - * @constructor - * @extends {io.Transport.websocket} - * @api public - */ - - function Flashsocket () { - io.Transport.websocket.apply(this, arguments); - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(Flashsocket, io.Transport.websocket); - - /** - * Transport name - * - * @api public - */ - - Flashsocket.prototype.name = 'flashsocket'; - - /** - * Disconnect the established `FlashSocket` connection. This is done by adding a - * new task to the FlashSocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.open = function () { - var self = this - , args = arguments; - - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.open.apply(self, args); - }); - return this; - }; - - /** - * Sends a message to the Socket.IO server. This is done by adding a new - * task to the FlashSocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.send = function () { - var self = this, args = arguments; - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.send.apply(self, args); - }); - return this; - }; - - /** - * Disconnects the established `FlashSocket` connection. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.close = function () { - WebSocket.__tasks.length = 0; - io.Transport.websocket.prototype.close.call(this); - return this; - }; - - /** - * The WebSocket fall back needs to append the flash container to the body - * element, so we need to make sure we have access to it. Or defer the call - * until we are sure there is a body element. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - Flashsocket.prototype.ready = function (socket, fn) { - function init () { - var options = socket.options - , port = options['flash policy port'] - , path = [ - 'http' + (options.secure ? 's' : '') + ':/' - , options.host + ':' + options.port - , options.resource - , 'static/flashsocket' - , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf' - ]; - - // Only start downloading the swf file when the checked that this browser - // actually supports it - if (!Flashsocket.loaded) { - if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') { - // Set the correct file based on the XDomain settings - WEB_SOCKET_SWF_LOCATION = path.join('/'); - } - - if (port !== 843) { - WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port); - } - - WebSocket.__initialize(); - Flashsocket.loaded = true; - } - - fn.call(self); - } - - var self = this; - if (document.body) return init(); - - io.util.load(init); - }; - - /** - * Check if the FlashSocket transport is supported as it requires that the Adobe - * Flash Player plug-in version `10.0.0` or greater is installed. And also check if - * the polyfill is correctly loaded. - * - * @returns {Boolean} - * @api public - */ - - Flashsocket.check = function () { - if ( - typeof WebSocket == 'undefined' - || !('__initialize' in WebSocket) || !swfobject - ) return false; - - return swfobject.getFlashPlayerVersion().major >= 10; - }; - - /** - * Check if the FlashSocket transport can be used as cross domain / cross origin - * transport. Because we can't see which type (secure or insecure) of .swf is used - * we will just return true. - * - * @returns {Boolean} - * @api public - */ - - Flashsocket.xdomainCheck = function () { - return true; - }; - - /** - * Disable AUTO_INITIALIZATION - */ - - if (typeof window != 'undefined') { - WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; - } - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('flashsocket'); -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/* SWFObject v2.2 - is released under the MIT License -*/ -if ('undefined' != typeof window) { -var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab -// License: New BSD License -// Reference: http://dev.w3.org/html5/websockets/ -// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol - -(function() { - - if ('undefined' == typeof window || window.WebSocket) return; - - var console = window.console; - if (!console || !console.log || !console.error) { - console = {log: function(){ }, error: function(){ }}; - } - - if (!swfobject.hasFlashPlayerVersion("10.0.0")) { - console.error("Flash Player >= 10.0.0 is required."); - return; - } - if (location.protocol == "file:") { - console.error( - "WARNING: web-socket-js doesn't work in file:///... URL " + - "unless you set Flash Security Settings properly. " + - "Open the page via Web server i.e. http://..."); - } - - /** - * This class represents a faux web socket. - * @param {string} url - * @param {array or string} protocols - * @param {string} proxyHost - * @param {int} proxyPort - * @param {string} headers - */ - WebSocket = function(url, protocols, proxyHost, proxyPort, headers) { - var self = this; - self.__id = WebSocket.__nextId++; - WebSocket.__instances[self.__id] = self; - self.readyState = WebSocket.CONNECTING; - self.bufferedAmount = 0; - self.__events = {}; - if (!protocols) { - protocols = []; - } else if (typeof protocols == "string") { - protocols = [protocols]; - } - // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. - // Otherwise, when onopen fires immediately, onopen is called before it is set. - setTimeout(function() { - WebSocket.__addTask(function() { - WebSocket.__flash.create( - self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null); - }); - }, 0); - }; - - /** - * Send data to the web socket. - * @param {string} data The data to send to the socket. - * @return {boolean} True for success, false for failure. - */ - WebSocket.prototype.send = function(data) { - if (this.readyState == WebSocket.CONNECTING) { - throw "INVALID_STATE_ERR: Web Socket connection has not been established"; - } - // We use encodeURIComponent() here, because FABridge doesn't work if - // the argument includes some characters. We don't use escape() here - // because of this: - // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions - // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't - // preserve all Unicode characters either e.g. "\uffff" in Firefox. - // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require - // additional testing. - var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); - if (result < 0) { // success - return true; - } else { - this.bufferedAmount += result; - return false; - } - }; - - /** - * Close this web socket gracefully. - */ - WebSocket.prototype.close = function() { - if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { - return; - } - this.readyState = WebSocket.CLOSING; - WebSocket.__flash.close(this.__id); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.addEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) { - this.__events[type] = []; - } - this.__events[type].push(listener); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) return; - var events = this.__events[type]; - for (var i = events.length - 1; i >= 0; --i) { - if (events[i] === listener) { - events.splice(i, 1); - break; - } - } - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {Event} event - * @return void - */ - WebSocket.prototype.dispatchEvent = function(event) { - var events = this.__events[event.type] || []; - for (var i = 0; i < events.length; ++i) { - events[i](event); - } - var handler = this["on" + event.type]; - if (handler) handler(event); - }; - - /** - * Handles an event from Flash. - * @param {Object} flashEvent - */ - WebSocket.prototype.__handleEvent = function(flashEvent) { - if ("readyState" in flashEvent) { - this.readyState = flashEvent.readyState; - } - if ("protocol" in flashEvent) { - this.protocol = flashEvent.protocol; - } - - var jsEvent; - if (flashEvent.type == "open" || flashEvent.type == "error") { - jsEvent = this.__createSimpleEvent(flashEvent.type); - } else if (flashEvent.type == "close") { - // TODO implement jsEvent.wasClean - jsEvent = this.__createSimpleEvent("close"); - } else if (flashEvent.type == "message") { - var data = decodeURIComponent(flashEvent.message); - jsEvent = this.__createMessageEvent("message", data); - } else { - throw "unknown event type: " + flashEvent.type; - } - - this.dispatchEvent(jsEvent); - }; - - WebSocket.prototype.__createSimpleEvent = function(type) { - if (document.createEvent && window.Event) { - var event = document.createEvent("Event"); - event.initEvent(type, false, false); - return event; - } else { - return {type: type, bubbles: false, cancelable: false}; - } - }; - - WebSocket.prototype.__createMessageEvent = function(type, data) { - if (document.createEvent && window.MessageEvent && !window.opera) { - var event = document.createEvent("MessageEvent"); - event.initMessageEvent("message", false, false, data, null, null, window, null); - return event; - } else { - // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. - return {type: type, data: data, bubbles: false, cancelable: false}; - } - }; - - /** - * Define the WebSocket readyState enumeration. - */ - WebSocket.CONNECTING = 0; - WebSocket.OPEN = 1; - WebSocket.CLOSING = 2; - WebSocket.CLOSED = 3; - - WebSocket.__flash = null; - WebSocket.__instances = {}; - WebSocket.__tasks = []; - WebSocket.__nextId = 0; - - /** - * Load a new flash security policy file. - * @param {string} url - */ - WebSocket.loadFlashPolicyFile = function(url){ - WebSocket.__addTask(function() { - WebSocket.__flash.loadManualPolicyFile(url); - }); - }; - - /** - * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. - */ - WebSocket.__initialize = function() { - if (WebSocket.__flash) return; - - if (WebSocket.__swfLocation) { - // For backword compatibility. - window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; - } - if (!window.WEB_SOCKET_SWF_LOCATION) { - console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); - return; - } - var container = document.createElement("div"); - container.id = "webSocketContainer"; - // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents - // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). - // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash - // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is - // the best we can do as far as we know now. - container.style.position = "absolute"; - if (WebSocket.__isFlashLite()) { - container.style.left = "0px"; - container.style.top = "0px"; - } else { - container.style.left = "-100px"; - container.style.top = "-100px"; - } - var holder = document.createElement("div"); - holder.id = "webSocketFlash"; - container.appendChild(holder); - document.body.appendChild(container); - // See this article for hasPriority: - // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html - swfobject.embedSWF( - WEB_SOCKET_SWF_LOCATION, - "webSocketFlash", - "1" /* width */, - "1" /* height */, - "10.0.0" /* SWF version */, - null, - null, - {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, - null, - function(e) { - if (!e.success) { - console.error("[WebSocket] swfobject.embedSWF failed"); - } - }); - }; - - /** - * Called by Flash to notify JS that it's fully loaded and ready - * for communication. - */ - WebSocket.__onFlashInitialized = function() { - // We need to set a timeout here to avoid round-trip calls - // to flash during the initialization process. - setTimeout(function() { - WebSocket.__flash = document.getElementById("webSocketFlash"); - WebSocket.__flash.setCallerUrl(location.href); - WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); - for (var i = 0; i < WebSocket.__tasks.length; ++i) { - WebSocket.__tasks[i](); - } - WebSocket.__tasks = []; - }, 0); - }; - - /** - * Called by Flash to notify WebSockets events are fired. - */ - WebSocket.__onFlashEvent = function() { - setTimeout(function() { - try { - // Gets events using receiveEvents() instead of getting it from event object - // of Flash event. This is to make sure to keep message order. - // It seems sometimes Flash events don't arrive in the same order as they are sent. - var events = WebSocket.__flash.receiveEvents(); - for (var i = 0; i < events.length; ++i) { - WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); - } - } catch (e) { - console.error(e); - } - }, 0); - return true; - }; - - // Called by Flash. - WebSocket.__log = function(message) { - console.log(decodeURIComponent(message)); - }; - - // Called by Flash. - WebSocket.__error = function(message) { - console.error(decodeURIComponent(message)); - }; - - WebSocket.__addTask = function(task) { - if (WebSocket.__flash) { - task(); - } else { - WebSocket.__tasks.push(task); - } - }; - - /** - * Test if the browser is running flash lite. - * @return {boolean} True if flash lite is running, false otherwise. - */ - WebSocket.__isFlashLite = function() { - if (!window.navigator || !window.navigator.mimeTypes) { - return false; - } - var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; - if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { - return false; - } - return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; - }; - - if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { - if (window.addEventListener) { - window.addEventListener("load", function(){ - WebSocket.__initialize(); - }, false); - } else { - window.attachEvent("onload", function(){ - WebSocket.__initialize(); - }); - } - } - -})(); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - * - * @api public - */ - - exports.XHR = XHR; - - /** - * XHR constructor - * - * @costructor - * @api public - */ - - function XHR (socket) { - if (!socket) return; - - io.Transport.apply(this, arguments); - this.sendBuffer = []; - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(XHR, io.Transport); - - /** - * Establish a connection - * - * @returns {Transport} - * @api public - */ - - XHR.prototype.open = function () { - this.socket.setBuffer(false); - this.onOpen(); - this.get(); - - // we need to make sure the request succeeds since we have no indication - // whether the request opened or not until it succeeded. - this.setCloseTimeout(); - - return this; - }; - - /** - * Check if we need to send data to the Socket.IO server, if we have data in our - * buffer we encode it and forward it to the `post` method. - * - * @api private - */ - - XHR.prototype.payload = function (payload) { - var msgs = []; - - for (var i = 0, l = payload.length; i < l; i++) { - msgs.push(io.parser.encodePacket(payload[i])); - } - - this.send(io.parser.encodePayload(msgs)); - }; - - /** - * Send data to the Socket.IO server. - * - * @param data The message - * @returns {Transport} - * @api public - */ - - XHR.prototype.send = function (data) { - this.post(data); - return this; - }; - - /** - * Posts a encoded message to the Socket.IO server. - * - * @param {String} data A encoded message. - * @api private - */ - - function empty () { }; - - XHR.prototype.post = function (data) { - var self = this; - this.socket.setBuffer(true); - - function stateChange () { - if (this.readyState == 4) { - this.onreadystatechange = empty; - self.posting = false; - - if (this.status == 200){ - self.socket.setBuffer(false); - } else { - self.onClose(); - } - } - } - - function onload () { - this.onload = empty; - self.socket.setBuffer(false); - }; - - this.sendXHR = this.request('POST'); - - if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) { - this.sendXHR.onload = this.sendXHR.onerror = onload; - } else { - this.sendXHR.onreadystatechange = stateChange; - } - - this.sendXHR.send(data); - }; - - /** - * Disconnects the established `XHR` connection. - * - * @returns {Transport} - * @api public - */ - - XHR.prototype.close = function () { - this.onClose(); - return this; - }; - - /** - * Generates a configured XHR request - * - * @param {String} url The url that needs to be requested. - * @param {String} method The method the request should use. - * @returns {XMLHttpRequest} - * @api private - */ - - XHR.prototype.request = function (method) { - var req = io.util.request(this.socket.isXDomain()) - , query = io.util.query(this.socket.options.query, 't=' + +new Date); - - req.open(method || 'GET', this.prepareUrl() + query, true); - - if (method == 'POST') { - try { - if (req.setRequestHeader) { - req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); - } else { - // XDomainRequest - req.contentType = 'text/plain'; - } - } catch (e) {} - } - - return req; - }; - - /** - * Returns the scheme to use for the transport URLs. - * - * @api private - */ - - XHR.prototype.scheme = function () { - return this.socket.options.secure ? 'https' : 'http'; - }; - - /** - * Check if the XHR transports are supported - * - * @param {Boolean} xdomain Check if we support cross domain requests. - * @returns {Boolean} - * @api public - */ - - XHR.check = function (socket, xdomain) { - try { - var request = io.util.request(xdomain), - usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest), - socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'), - isXProtocol = (socketProtocol != global.location.protocol); - if (request && !(usesXDomReq && isXProtocol)) { - return true; - } - } catch(e) {} - - return false; - }; - - /** - * Check if the XHR transport supports cross domain requests. - * - * @returns {Boolean} - * @api public - */ - - XHR.xdomainCheck = function (socket) { - return XHR.check(socket, true); - }; - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.htmlfile = HTMLFile; - - /** - * The HTMLFile transport creates a `forever iframe` based transport - * for Internet Explorer. Regular forever iframe implementations will - * continuously trigger the browsers buzy indicators. If the forever iframe - * is created inside a `htmlfile` these indicators will not be trigged. - * - * @constructor - * @extends {io.Transport.XHR} - * @api public - */ - - function HTMLFile (socket) { - io.Transport.XHR.apply(this, arguments); - }; - - /** - * Inherits from XHR transport. - */ - - io.util.inherit(HTMLFile, io.Transport.XHR); - - /** - * Transport name - * - * @api public - */ - - HTMLFile.prototype.name = 'htmlfile'; - - /** - * Creates a new Ac...eX `htmlfile` with a forever loading iframe - * that can be used to listen to messages. Inside the generated - * `htmlfile` a reference will be made to the HTMLFile transport. - * - * @api private - */ - - HTMLFile.prototype.get = function () { - this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); - this.doc.open(); - this.doc.write(''); - this.doc.close(); - this.doc.parentWindow.s = this; - - var iframeC = this.doc.createElement('div'); - iframeC.className = 'socketio'; - - this.doc.body.appendChild(iframeC); - this.iframe = this.doc.createElement('iframe'); - - iframeC.appendChild(this.iframe); - - var self = this - , query = io.util.query(this.socket.options.query, 't='+ +new Date); - - this.iframe.src = this.prepareUrl() + query; - - io.util.on(window, 'unload', function () { - self.destroy(); - }); - }; - - /** - * The Socket.IO server will write script tags inside the forever - * iframe, this function will be used as callback for the incoming - * information. - * - * @param {String} data The message - * @param {document} doc Reference to the context - * @api private - */ - - HTMLFile.prototype._ = function (data, doc) { - this.onData(data); - try { - var script = doc.getElementsByTagName('script')[0]; - script.parentNode.removeChild(script); - } catch (e) { } - }; - - /** - * Destroy the established connection, iframe and `htmlfile`. - * And calls the `CollectGarbage` function of Internet Explorer - * to release the memory. - * - * @api private - */ - - HTMLFile.prototype.destroy = function () { - if (this.iframe){ - try { - this.iframe.src = 'about:blank'; - } catch(e){} - - this.doc = null; - this.iframe.parentNode.removeChild(this.iframe); - this.iframe = null; - - CollectGarbage(); - } - }; - - /** - * Disconnects the established connection. - * - * @returns {Transport} Chaining. - * @api public - */ - - HTMLFile.prototype.close = function () { - this.destroy(); - return io.Transport.XHR.prototype.close.call(this); - }; - - /** - * Checks if the browser supports this transport. The browser - * must have an `Ac...eXObject` implementation. - * - * @return {Boolean} - * @api public - */ - - HTMLFile.check = function (socket) { - if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){ - try { - var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); - return a && io.Transport.XHR.check(socket); - } catch(e){} - } - return false; - }; - - /** - * Check if cross domain requests are supported. - * - * @returns {Boolean} - * @api public - */ - - HTMLFile.xdomainCheck = function () { - // we can probably do handling for sub-domains, we should - // test that it's cross domain but a subdomain here - return false; - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('htmlfile'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports['xhr-polling'] = XHRPolling; - - /** - * The XHR-polling transport uses long polling XHR requests to create a - * "persistent" connection with the server. - * - * @constructor - * @api public - */ - - function XHRPolling () { - io.Transport.XHR.apply(this, arguments); - }; - - /** - * Inherits from XHR transport. - */ - - io.util.inherit(XHRPolling, io.Transport.XHR); - - /** - * Merge the properties from XHR transport - */ - - io.util.merge(XHRPolling, io.Transport.XHR); - - /** - * Transport name - * - * @api public - */ - - XHRPolling.prototype.name = 'xhr-polling'; - - /** - * Indicates whether heartbeats is enabled for this transport - * - * @api private - */ - - XHRPolling.prototype.heartbeats = function () { - return false; - }; - - /** - * Establish a connection, for iPhone and Android this will be done once the page - * is loaded. - * - * @returns {Transport} Chaining. - * @api public - */ - - XHRPolling.prototype.open = function () { - var self = this; - - io.Transport.XHR.prototype.open.call(self); - return false; - }; - - /** - * Starts a XHR request to wait for incoming messages. - * - * @api private - */ - - function empty () {}; - - XHRPolling.prototype.get = function () { - if (!this.isOpen) return; - - var self = this; - - function stateChange () { - if (this.readyState == 4) { - this.onreadystatechange = empty; - - if (this.status == 200) { - self.onData(this.responseText); - self.get(); - } else { - self.onClose(); - } - } - }; - - function onload () { - this.onload = empty; - this.onerror = empty; - self.onData(this.responseText); - self.get(); - }; - - function onerror () { - self.onClose(); - }; - - this.xhr = this.request(); - - if (global.XDomainRequest && this.xhr instanceof XDomainRequest) { - this.xhr.onload = onload; - this.xhr.onerror = onerror; - } else { - this.xhr.onreadystatechange = stateChange; - } - - this.xhr.send(null); - }; - - /** - * Handle the unclean close behavior. - * - * @api private - */ - - XHRPolling.prototype.onClose = function () { - io.Transport.XHR.prototype.onClose.call(this); - - if (this.xhr) { - this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty; - try { - this.xhr.abort(); - } catch(e){} - this.xhr = null; - } - }; - - /** - * Webkit based browsers show a infinit spinner when you start a XHR request - * before the browsers onload event is called so we need to defer opening of - * the transport until the onload event is called. Wrapping the cb in our - * defer method solve this. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - XHRPolling.prototype.ready = function (socket, fn) { - var self = this; - - io.util.defer(function () { - fn.call(self); - }); - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('xhr-polling'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - /** - * There is a way to hide the loading indicator in Firefox. If you create and - * remove a iframe it will stop showing the current loading indicator. - * Unfortunately we can't feature detect that and UA sniffing is evil. - * - * @api private - */ - - var indicator = global.document && "MozAppearance" in - global.document.documentElement.style; - - /** - * Expose constructor. - */ - - exports['jsonp-polling'] = JSONPPolling; - - /** - * The JSONP transport creates an persistent connection by dynamically - * inserting a script tag in the page. This script tag will receive the - * information of the Socket.IO server. When new information is received - * it creates a new script tag for the new data stream. - * - * @constructor - * @extends {io.Transport.xhr-polling} - * @api public - */ - - function JSONPPolling (socket) { - io.Transport['xhr-polling'].apply(this, arguments); - - this.index = io.j.length; - - var self = this; - - io.j.push(function (msg) { - self._(msg); - }); - }; - - /** - * Inherits from XHR polling transport. - */ - - io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); - - /** - * Transport name - * - * @api public - */ - - JSONPPolling.prototype.name = 'jsonp-polling'; - - /** - * Posts a encoded message to the Socket.IO server using an iframe. - * The iframe is used because script tags can create POST based requests. - * The iframe is positioned outside of the view so the user does not - * notice it's existence. - * - * @param {String} data A encoded message. - * @api private - */ - - JSONPPolling.prototype.post = function (data) { - var self = this - , query = io.util.query( - this.socket.options.query - , 't='+ (+new Date) + '&i=' + this.index - ); - - if (!this.form) { - var form = document.createElement('form') - , area = document.createElement('textarea') - , id = this.iframeId = 'socketio_iframe_' + this.index - , iframe; - - form.className = 'socketio'; - form.style.position = 'absolute'; - form.style.top = '0px'; - form.style.left = '0px'; - form.style.display = 'none'; - form.target = id; - form.method = 'POST'; - form.setAttribute('accept-charset', 'utf-8'); - area.name = 'd'; - form.appendChild(area); - document.body.appendChild(form); - - this.form = form; - this.area = area; - } - - this.form.action = this.prepareUrl() + query; - - function complete () { - initIframe(); - self.socket.setBuffer(false); - }; - - function initIframe () { - if (self.iframe) { - self.form.removeChild(self.iframe); - } - - try { - // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) - iframe = document.createElement('