Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/extensions/scratch3_mesh_v2/mesh-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class MeshV2Service {
this.dataSyncTimer = null;
this.memberHeartbeatInterval = 120; // Default 2 min

// Data from other nodes: { nodeId: { key: value } }
// Data from other nodes: { nodeId: { key: { value: string, timestamp: number } } }
this.remoteData = {};

// Rate limiters
Expand Down Expand Up @@ -365,7 +365,10 @@ class MeshV2Service {
}

nodeStatus.data.forEach(item => {
this.remoteData[nodeId][item.key] = item.value;
this.remoteData[nodeId][item.key] = {
value: item.value,
timestamp: Date.now() // Add timestamp
};
});
}

Expand Down Expand Up @@ -775,7 +778,10 @@ class MeshV2Service {
this.remoteData[status.nodeId] = {};
}
status.data.forEach(item => {
this.remoteData[status.nodeId][item.key] = item.value;
this.remoteData[status.nodeId][item.key] = {
value: item.value,
timestamp: Date.now()
};
});
});

Expand Down Expand Up @@ -848,13 +854,20 @@ class MeshV2Service {
}

getRemoteVariable (name) {
let latestValue = null;
let latestTimestamp = 0;

// Search across all nodes for the variable name
for (const nodeId in this.remoteData) {
if (Object.prototype.hasOwnProperty.call(this.remoteData[nodeId], name)) {
return this.remoteData[nodeId][name];
const data = this.remoteData[nodeId][name];
if (data.timestamp > latestTimestamp) {
latestTimestamp = data.timestamp;
latestValue = data.value;
}
}
}
return null;
return latestValue;
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/integration/extensions/mesh-v2-variable-sync.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ test('MeshV2Service fetch existing nodes data on joinGroup', async t => {
await service.joinGroup('group1', 'domain1', 'groupName');

t.ok(service.remoteData['host-node'], 'Should have data from host-node');
t.equal(service.remoteData['host-node'].hostVar, '100', 'Should have correct variable value from host');
t.equal(service.remoteData['host-node'].hostVar.value, '100', 'Should have correct variable value from host');

service.cleanup();
t.end();
Expand Down
84 changes: 84 additions & 0 deletions test/unit/mesh_service_v2_timestamp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const test = require('tap').test;
const MeshV2Service = require('../../src/extensions/scratch3_mesh_v2/mesh-service');

const createMockBlocks = () => ({
runtime: {
sequencer: {},
emit: () => {},
on: () => {},
off: () => {}
}
});

test('MeshV2Service Timestamp-based getRemoteVariable', t => {
const blocks = createMockBlocks();
const service = new MeshV2Service(blocks, 'node-self', 'domain1');
service.groupId = 'group1';

t.test('should return the latest value based on timestamp', st => {
// Setup remoteData with multiple nodes having the same key
const now = Date.now();
service.remoteData = {
node1: {
'my var': {value: 'value-old', timestamp: now - 1000}
},
node2: {
'my var': {value: 'value-newest', timestamp: now}
},
node3: {
'my var': {value: 'value-middle', timestamp: now - 500}
}
};

const result = service.getRemoteVariable('my var');
st.equal(result, 'value-newest', 'Should return the value with the largest timestamp');
st.end();
});

t.test('handleDataUpdate should add timestamp', st => {
const nodeStatus = {
nodeId: 'node4',
data: [
{key: 'var1', value: '100'}
]
};

const beforeUpdate = Date.now();
service.handleDataUpdate(nodeStatus);
const afterUpdate = Date.now();

st.ok(service.remoteData.node4, 'Node 4 should be added');
st.ok(service.remoteData.node4.var1, 'var1 should be added');
st.equal(service.remoteData.node4.var1.value, '100');
st.ok(service.remoteData.node4.var1.timestamp >= beforeUpdate);
st.ok(service.remoteData.node4.var1.timestamp <= afterUpdate);
st.end();
});

t.test('fetchAllNodesData should add timestamp', async st => {
service.client = {
query: () => Promise.resolve({
data: {
listGroupStatuses: [
{
nodeId: 'node5',
data: [{key: 'var2', value: '200'}]
}
]
}
})
};

const beforeFetch = Date.now();
await service.fetchAllNodesData();
const afterFetch = Date.now();

st.ok(service.remoteData.node5, 'Node 5 should be added');
st.equal(service.remoteData.node5.var2.value, '200');
st.ok(service.remoteData.node5.var2.timestamp >= beforeFetch);
st.ok(service.remoteData.node5.var2.timestamp <= afterFetch);
st.end();
});

t.end();
});
Loading