How to delay event emission with rxpy/rxjs? - python-3.x

I've got two event streams. One is from an inductance loop, the other is an IP camera. Cars will drive over the loop and then hit the camera. I want to combine them if the events are within N milliseconds of each other (car will always hit the loop first), but I also want the unmatched events from each stream (either hardware can fail) all merged into a single stream. Something like this:
---> (only unmatched a's, None)
/ \
stream_a (loop) \
\ \
--> (a, b) ---------------------------> (Maybe a, Maybe b)
/ /
stream_b (camera) /
\ /
--> (None, only unmatched b's)
Now certainly I can hack my way around by doing the good ole Subject anti-pattern:
unmatched_a = Subject()
def noop():
pass
pending_as = [[]]
def handle_unmatched(a):
if a in pending_as[0]:
pending_as[0].remove(a)
print("unmatched a!")
unmatched_a.on_next((a, None))
def handle_a(a):
pending_as[0].append(a)
t = threading.Timer(some_timeout, handle_unmatched)
t.start()
return a
def handle_b(b):
if len(pending_as[0]):
a = pending_as[0].pop(0)
return (a, b)
else:
print("unmatched b!")
return (None, b)
stream_a.map(handle_a).subscribe(noop)
stream_b.map(handle_b).merge(unmatched_a).subscribe(print)
Not only is this rather hacky, but although I've not observed it I'm pretty sure there's a race condition when I check the pending queue using threading.Timer. Given the plethora of rx operators, I'm pretty sure some combination of them will let you do this without using Subject, but I can't figure it out. How does one accomplish this?
Edit
Although for organizational and operational reasons I'd prefer to stick to Python, I'll take a JavaScript rxjs answer and either port it or even possibly rewrite the entire script in node.

You should be able to solve the problem using auditTime and buffer. Like this:
function matchWithinTime(a$, b$, N) {
const merged$ = Rx.Observable.merge(a$, b$);
// Use auditTime to compose a closing notifier for the buffer.
const audited$ = merged$.auditTime(N);
// Buffer emissions within an audit and filter out empty buffers.
return merged$
.buffer(audited$)
.filter(x => x.length > 0);
}
const a$ = new Rx.Subject();
const b$ = new Rx.Subject();
matchWithinTime(a$, b$, 50).subscribe(x => console.log(JSON.stringify(x)));
setTimeout(() => a$.next("a"), 0);
setTimeout(() => b$.next("b"), 0);
setTimeout(() => a$.next("a"), 100);
setTimeout(() => b$.next("b"), 125);
setTimeout(() => a$.next("a"), 200);
setTimeout(() => b$.next("b"), 275);
setTimeout(() => a$.next("a"), 400);
setTimeout(() => b$.next("b"), 425);
setTimeout(() => a$.next("a"), 500);
setTimeout(() => b$.next("b"), 575);
setTimeout(() => b$.next("b"), 700);
setTimeout(() => b$.next("a"), 800);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs#5/bundles/Rx.min.js"></script>
If it's possible for b values to be closely followed by a values and you do not want them to be matched, you could use a more specific audit, like this:
const audited$ = merged$.audit(x => x === "a" ?
// If an `a` was received, audit upcoming values for `N` milliseconds.
Rx.Observable.timer(N) :
// If a `b` was received, don't audit the upcoming values.
Rx.Observable.of(0, Rx.Scheduler.asap)
);

I have developed a different strategy than Cartant, and clearly much less elegant, which may give you somehow a different result. I apologize if I have not understood the question and if my answer turns out to be useless.
My strategy is based on using switchMap on a$ and then bufferTime on b$.
This code emits at every timeInterval and it emits an object which contains the last a received and an array of bs representing the bs received during the time interval.
a$.pipe(
switchMap(a => {
return b$.pipe(
bufferTime(timeInterval),
mergeMap(arrayOfB => of({a, arrayOfB})),
)
})
)
If arrayOfB is empty, than it means that the last a in unmatched.
If arrayOfB has just one element, than it means that the last a has been matched by the b of the array.
If arrayOfB has more than one element, than it means that the last a has been matched by the first b of the array while all other bs are unmatched.
Now it is a matter of avoiding the emission of the same a more than
once and this is where the code gets a bit messy.
In summary, the code could look like the following
const a$ = new Subject();
const b$ = new Subject();
setTimeout(() => a$.next("a1"), 0);
setTimeout(() => b$.next("b1"), 0);
setTimeout(() => a$.next("a2"), 100);
setTimeout(() => b$.next("b2"), 125);
setTimeout(() => a$.next("a3"), 200);
setTimeout(() => b$.next("b3"), 275);
setTimeout(() => a$.next("a4"), 400);
setTimeout(() => b$.next("b4"), 425);
setTimeout(() => b$.next("b4.1"), 435);
setTimeout(() => a$.next("a5"), 500);
setTimeout(() => b$.next("b5"), 575);
setTimeout(() => b$.next("b6"), 700);
setTimeout(() => b$.next("b6.1"), 701);
setTimeout(() => b$.next("b6.2"), 702);
setTimeout(() => a$.next("a6"), 800);
setTimeout(() => a$.complete(), 1000);
setTimeout(() => b$.complete(), 1000);
let currentA;
a$.pipe(
switchMap(a => {
currentA = a;
return b$.pipe(
bufferTime(50),
mergeMap(arrayOfB => {
let aVal = currentA ? currentA : null;
if (arrayOfB.length === 0) {
const ret = of({a: aVal, b: null})
currentA = null;
return ret;
}
if (arrayOfB.length === 1) {
const ret = of({a: aVal, b: arrayOfB[0]})
currentA = null;
return ret;
}
const ret = from(arrayOfB)
.pipe(
map((b, _indexB) => {
aVal = _indexB > 0 ? null : aVal;
return {a: aVal, b}
})
)
currentA = null;
return ret;
}),
filter(data => data.a !== null || data.b !== null)
)
})
)
.subscribe(console.log);

Related

Dynamic test data for test.each Jest

Is it possible to assign values at runtime for below Jest test.each example:
describe('jb-tests', () => {
jest.setTimeout(700000);
let table: Array<number[][]> = [[]];
beforeAll(() => {
//Below lines are just hardcoded values
table = [];
let test = [[1,2,3],[4,5,6],[7,8,9]]
table.push(test);
});
test.each(table)('.add(%i, %i)', (a, b, expected) => {
console.log("inside");
});
});
This test case is stuck and not showing any output. If I remove the jest.setTimeout then it fails with message "Exceeded timeout of 5000 ms for a test."
Building on the comment from #jonrsharpe, you can however specify values in the test.each() call.
describe('jb-tests', () => {
jest.setTimeout(700000);
test.each([
[1,2,3],
[4,5,6],
[7,8,9],
])('.add(%i, %i)', (a, b, expected) => {
expect(a + b).toBe(Expected);
});
});
However, if you really need data from an external source, and want to process it, you can load it in the test code, or a beforeAll(). Note that the beforeAll() is evaluated at the very start, so what is loaded there is done at the start of processing the file, not as each test is run.
import * as NodeJSFS from 'fs/promises';
describe('jb-tests', () => {
jest.setTimeout(700000);
let table: Array<number[][]> = [[]];
beforeAll(async () => {
const FileContent$: Buffer = await NodeJSFSPromises.readFile('testdata.txt');
// Loop through FileContent$ (map, etc.) and load your table (table.push)
...
});
describe('adding addition describe for this section', () => {
for (const data of table) {
... // Parse data (one element of table)
it(`should add ${a} + ${b} to be ${Expected}', () => {
expect(a + b).toBe(Expected);
});
}
});
Just found an alternate solution: Populate the test data dynamically and then use describe block to execute test cases using test.each.
let test_data: Array<number[]> = [];
for(let i=0;i<3;i++){
test_data.push([i+0,i+1,i+2]);
}
describe('jb-tests', () => {
test.each(test_data)('.add(%i, %i, %i)', (a, b, c) => {
expect(a + b + c).toBeGreaterThan(0);
});
});

Why is my console logging: `TimeoutOverflowWarning: 4294967296000 does not fit into a 32-bit signed integer`

I am following the course stream-adventure. One of the assignments is to make an http server which converts all the requests to uppercase and return it in the response.
Now I managed to get it working and the assignment passes. However, the console gives me a TimeoutOverflowWarning.
(node:15710) TimeoutOverflowWarning: 4294967296000 does not fit into a 32-bit signed integer.
Timer duration was truncated to 2147483647.
(node:15710) TimeoutOverflowWarning: 4294967296000 does not fit into a 32-bit signed integer.
Timer duration was truncated to 2147483647.
I'm wondering if it's a memory leak or something caused by my code, or if it is something else. Because in the error message 32-bit is mentioned, I wonder if it's related to that I'm using a Macbook Pro from 2016 which runs in 64 bit. (node v10.17.0)
The code:
'use-strict'
const through = require('through2')
const http = require('http')
const port = process.argv[2]
const uppercaser = through(function (buffer, _, next) {
this.push(buffer.toString().toUpperCase())
next()
});
const server = http.createServer(function (req, res) {
if (req.method === 'POST') {
res.writeHead(200, { 'Content-Type': 'text/plain' })
req.pipe(uppercaser).pipe(res)
} else {
res.writeHead(404)
res.end()
}
});
server.listen(port)
Google searches give various causes of this problem (example 1, example 2) and it seems that most of the solutions are fixed in library used.
When I find this type of error is when I use a setInterval function.
This is because the upper limit of the timeout is 2147483647 which is the max limit of 32-bit int.
If you want you can make your own setInterval wrapper
function setDaysTimeout(callback,days) {
// 86400 seconds in a day
let msInDay = 86400*1000;
let dayCount = 0;
let timer = setInterval(function() {
dayCount++; // a day has passed
if (dayCount === days) {
clearInterval(timer);
callback.apply(this, []);
}
}, msInDay);
}
And use it like this:
setDaysTimeout(function() {
console.log('Four days gone');
}, 4); // fire after 4 days
This is the problem related to setTimeout \ setInterval functions. They have a limit of 32-bit as you can see. So that the node is warning you about it:
> setTimeout(console.log, +Infinity)
> (node:19903) TimeoutOverflowWarning: Infinity does not fit into a 32-bit signed integer.
Timeout duration was set to 1.
Since your code does not have any of those, it seems like the problem with some code in library.
I'd recommend to run node with --trace-warnings flag to find the source of warning:
--trace-warnings
Print stack traces for process warnings (including deprecations).
The upper limit of the timeout is 2147483647, which is the max limit of 32-bit int. In order to avoid this you can wait for the max time and then wait for any remaining time.
const timeout = async (callback, time, callbackArguments) => {
if (!callback || typeof callback !== 'function') throw new Error('Invalid Callback')
let args = ((callbackArguments && typeof callbackArguments === 'object' &&
callbackArguments.length > 0) ? callbackArguments : [])
let max = 2147483647
if (time > max) {
let t = Math.floor(time / max)
let r = time%max
for (let i = 0; i < t; i++) await (() => new Promise(res => setTimeout(() => res(), max)))();
if (r) {
return setTimeout(() => callback(...args), r)
} else {
return callback(...args)
}
} else {
return setTimeout(() => callback(...args), time)
}
}
Use like so
let a = 2
let b = 5
let c = (a, b) => console.log(!a && !b ? 0 : !a && b ? b : a && !b ? a : a + b)
let now = new Date().getTime()
let later = now + 2147483647*5
timeout(c, later-now, [a,b])

node js normal callback scenario for blocking code

I am getting into callbacks(do not want to use promise and async-await right now) and wrote code in node.js to sum a, b and c where I have created three functions for getting values of a, b and c.
// getting value of a
const getA = () => 10
// getting for value b
const getB = () => 20
// This function has to wait for 2 seconds to
const getC = (callback) => {
setTimeout(() => {
callback(30)
}, 2000);
}
const sum = (a, b, c, callback) => {
callback(a + b + c)
}
sum(getA(), getB(), getC((c) => c), (sum) => console.log(sum))
I want output 60 after waiting for getC() to finish execution, but didn't get any
I really suggest to use promises. I know there are some corner cases, where you can't. Why? Because we used to think in a way that the code is linear, and it is easier to the brain to process.
But for your question I would give a solution.
// getting value of a
const getA = () => 10;
// getting for value b
const getB = () => 20;
// This function has to wait for 2 seconds to
const getC = (callback) => {
setTimeout(() => {
callback(30);
}, 2000);
};
getC((val) => console.log(getA() + getB() + val));
So you wait until getC has finished, and use the callback to consume the c result.

Firestore-Cloud Functions get a top layer file name

Have a nice day. First;
I would like to thank this user.
https://stackoverflow.com/a/52982781/11320646
I want you to know that I'm new to writing code. I have to train a project.
The function of these codes(include link, "solution" tittle) is this:
customers/{id} counts the consumers here and write count here:
metadatas/customers, {count:123456}.
But i want to add counter here:
groups/{groupID}/posts/{postID}
and ı should write count here:
metadatas/{groupID}/counts, {count:123456}
But these codes write:
metadatas/posts, {count:123456}
How to add the groupID layer?
I've tried many things, but I haven't succeeded.
I have successfully done counting with these codes, I have activated the retry option if it fails. I got a great result. At the same time more than 100 post was added and the result is excellent.
my codes are here:
const executeOnce = (change, context, task) => {
const eventRef = firestore.collection('events').doc(context.eventId);
return firestore.runTransaction(t =>
t
.get(eventRef)
.then(docSnap => (docSnap.exists ? null : task(t)))
.then(() => t.set(eventRef, { processed: true }))
);
};
const documentCounter = collectionName => (change, context) =>
executeOnce(change, context, t => {
// on create
if (!change.before.exists && change.after.exists) {
return t
.get(firestore.collection('counts')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: ((docSnap.data() && docSnap.data().count) || 0) + 1
}));
// on delete
} else if (change.before.exists && !change.after.exists) {
return t
.get(firestore.collection('counts')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: docSnap.data().count - 1
}));
}
return null;
});
exports.customersCounter = functions.firestore
.document('denemeicerikler/{grupID}/icerikler/{postID}')
.onWrite(documentCounter('icerikler'));

Cannot extract/unwrap value from concatenated Observable

I have this chain which should be concatenating 10 Observables into 1 Observable, where each of the 10 Observables should basically just be unwrapped to an integer:
const Rx = require('rxjs');
var i = 0;
const obs = Rx.Observable.interval(10)
.map(() => i++)
.map(val => Rx.Observable.create(obs => {
obs.next(val)
}))
.take(10)
.reduce((prev, curr) => {
return prev.concat(curr); // concat all observables
})
.last(val => val.flatMap(inner => inner));
// subscribe to Observable
obs.subscribe(v => {
console.log('\n next (and only) result => \n', v);
});
What's happening is that all 10 Observables should be concatenated together, but I cannot extract the values from those 10 Observables (which have become 1 Observable). So my question is, how can I unwrap that final observable and extract the value?
Anyone know what I am talking about?
This question has the exact problem as in Observable.prototype.concatAll does not seem to yield expected result
.map(function(val){ // note: map *not* flatMap
return Rx.Observable.create(obs => {
obs.next(val)
});
})
When creating your own observable using Rx.Observable.create you need to .complete() it yourself. Because you forgot to do so operators like .reduce() will not work because they need to wait for completion before being able to run.
Furthermore, your use of the .last() operator is incorrect; it takes a predicate on which your stream will be filtered and the last emission matching the predicate will be emitted. It is also a bit redundant because your .reduce() will only emit one value. Cleaning it up would lead to:
const obs = Rx.Observable.interval(10)
.map(() => i++)
.map(val => Rx.Observable.of(val))
.take(10)
.reduce((acc, curr) => acc.concat(curr))
.flatMap(v => v)
.toArray()
But you can shorten this by directly using the .concatMap() operator instead of map+take+reduce+flatMap:
const obs = Rx.Observable.interval(10)
.map(() => i++)
.concatMap(val => Rx.Observable.of(val))
.take(10)
.toArray()
Instead of reduce + last, you can use concatAll (or concatMap) to concatenate inner sequences and preserve order:
Observable
.interval(0)
.map(i => Observable.of(i))
.take(10)
.concatAll()
.takeLast(1)
.subscribe(v => console.log('\n next (and only) result => \n', v);)
edit: takeLast(1) instead of finalValue()
Use the non-prototype method.
let range = [...Array(10).keys()];
//array of 10 observables of 10 values each
let ten = range.map(i => Rx.Observable.interval(1000).map(_ => i).take(10));
let sequence = Rx.Observable.concat(ten) // all of them in sequence

Resources