Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guru mediditation #1

Open
dpharris opened this issue Jul 22, 2019 · 5 comments
Open

Guru mediditation #1

dpharris opened this issue Jul 22, 2019 · 5 comments

Comments

@dpharris
Copy link

Hi --

I tried you FastClock example, and adapted it for an OLED display. However, it kept crashing. I think the problem boiled down to a missing wiThrottle.begin(&Serial) in:
void wifiOnConnect() {
...
Serial.println("connected succeeded");
wiThrottle.begin(&Serial);
wiThrottle.connect(&client);
...
}

Also, I had to comment out:
// if (wiThrottle.protocolVersionChanged) {
// Serial.print("PROTOCOL VERSION "); Serial.println(wiThrottle.protocolVersion);
// }
I think you moved that into the delegation class.

I do have a question, though. How does one program the delegation routines, or rather 'connect' them to the class WiThrottleDelegate? Do I need to subclass that? Or just write WiThrottleDelegate::receivedSpeed(int speed) { }, for example?

Thanks
David

@davidzuhn
Copy link
Owner

The delegate class WiThrottleDelegate is intended as an interface class designation, and some other class that does real work is expected to inherit from that interface class. Well, C++ uses inheritance for this, at least. I've been doing a lot of work in iOS development lately, and the delegate pattern is all over the place in their APIs.

The point is that the WiThrottle protocol code does not know anything about what to do when various activities occur. Your code needs to do the right thing -- update a clock display, or show something on a UI screen for function state, etc. You pass in a reference to the controller class, which implements the delegate interface, and then the WiThrottle code will call code in the controller class (via that delegate interface) and your controller can do whatever it needs to.

This keeps the WiThrottle class simple -- all it does it generic stream I/O, command parsing & creation, and delegate calling.

It would be extremely rare to see an object of WiThrottleDelegate, or that doesn't do something else besides the WiThrottle delegate methods.

I've not been doing much with this codebase, since my project that needs WiThrottle support is no longer based on the Arduino interfaces, and so the String & Stream classes aren't available to me anymore. I've got a newer (very similar) codebase that's a little more generic C++, which I'm using with the ESP32 IDF.

So in Arduino land, you might have a ClockInterface class, which inherits from WiThrottleDelegate. The setup() function would create a clockInterface object, and then assign that into the wiThrottle.delegate member variable). Some code, maybe the clockInterface, maybe not, is responsible for the network setup and establishing that Stream and passing it into the WiThrottle parser. Each time you call check() [which should be A LOT], the WiThrottle code checks to see if there is any new data to read on that Stream, and if there is, it'll read it and try to form a command out of it. There's a buffer, so incomplete messages can be read and processed correctly.

My new code doesn't even do the I/O itself, you don't call check() on it, you pass in the data that you read, so the parser is completely free of external I/O. (Yes, that parser only reads, sending commands is done in a different blob of code -- perhaps I ought to combine them again, and have a delegate method that takes a complete command and handles all of the I/O needed to send the data). [That'll go on my todo list].

@dpharris
Copy link
Author

dpharris commented Jul 22, 2019 via email

@dpharris dpharris reopened this Jul 22, 2019
@davidzuhn
Copy link
Owner

Nope. That's mixing functions & classes, and you can't do that.

You can have a class like this:

class ClockController : public WiThrottleDelegate
{
void begin(int i2cAddr) { // do something to initialize the display at i2cAddr, for example }
void receivedFunctionState(uint8_t func, bool state) {
Serial.print("\n Received a function update! ");
// now use the clock display at i2cAddr to display the function state you just received
}

void fastTimeChanged(uint32_t time) {
// now display the new time that you've just been given
}

}

WiThrottle wiThrottle;
ClockController clockController;

void setup() {
clockController.begin(0x70);
wiThrottle.delegate = clockController;

// do what you need to do in order to get a Stream, such as open a TCP socket to the server
wiThrottle.begin(stream);
}

void loop() {
wiThrottle.check();
}

Now, in reality, you probably need to implement your own loop inside of loop(), since the TCP socket may drop and you need to re-open it after setup() has been called. This is one of the reasons I abandoned the Arduino framework. Robust networking code is hard to do, and Arduino doesn't help you a whole lot. Simple stuff works okay, if you're willing to power-cycle to re-connect to the JMRI server.

@davidzuhn
Copy link
Owner

Note that what you wrote for receivedFunctionState and what I wrote look a lot alike, but one is part of a class and one is not. That's all the difference, since the delegate is a class (with defined methods).

@dpharris
Copy link
Author

dpharris commented Jul 22, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants