Following on from the previous post, I’ve now made the changes to allow the Severin’s dc motor (fan) to be controlled separately from the ac heater coil. (Basically you snip the circuit after the rectifier and connect your own dc supply to the pins). This means the heater piece and the motor+fan are completely separate.
Along the way I found out a few things about the Severin and thought I’d write it up.
IMPORTANT: There’s a bunch of pictures of extremely dangerous live circuits with cheap equipment here. I have some safeties (RCD plug, rubber gloves, earthed casing on dimmer checked each time), and this is still very dangerous. Please don’t copy anything I’ve done.
So I basically disconnected all the bits and put it together outside the casing. I did this for ease of fiddling and rustic appeal. I mounted it on a bean tin can full of stones for stability, with some holes drilled for airflow to the fan.
The DC fan motor on a Severin is an RS-385SA-2065R. I think this is rated 18v (see the production description tab on this page)
I found I could jack the motor up to 30v (as high as my cheap bench supply goes) where it draws around 3 amps. According to the spec the stall load at 18v is 5.52 amps, so I’m a bit under that. At this voltage it will easily agitate 100g of greens without assistance, and probably quite a bit more. I should do some experimenting to see how high I can take the bean mass.
I should note I don’t have any experience with electric motors - I’m guessing that abusing the voltage like this will cause it to break at some point. Does anybody have opinions/experience of running cheap motors above their rated voltage?
I guess I also might be able to replace the motor with something more powerful.
The thermal fuse below the heating element in the Severin is rated for 240 degrees C (and is behind a card heat shield), so I guess that’s why you can use a Severin popper for coffee roasting out of the box without modding it. From reading accounts of other popcorn makers it seems that most have fuses at lower temperatures and these need to be bypassed.
I experimented with connecting the heater wires up to mains AC and found that black + brown cause both heating elements to glow so I snipped the grey wire.
After snipping the grey wire:
Heating element out of case:
The elements heat up really fast and hot. I only dared to test this for a couple of seconds without the fan (here’s a picture of it at 166v, with the dimmer module from the previous post controlling the ac voltage)
After mounting it on the tin can and checking the grounding, I took the unit outside to roast 100g of Colombia Excelso Huila from Redber.
The outside temp was about 9c today and a bit windy. I started with the fan jacked up to 30v/3a for maximum agitation and the heater at 207v. I then increased the temp of the beans by increasing the heater voltage until it maxed out at 237v about 5 mins in. Unfortunately the roast started stalling so with no more heating power I had to reduce the fan motor to increase the bean mass temp. I slowly reduced the fan motor in stages until the end of the roast (fan motor is yellow line/right axis in the chart below, bean mass temp is the blue line). I dumped the beans after reaching 214c at 20 mins.
The roast was much more even through all the stages than before, but I was suffering from a lack of heat. The popper in its original state was easily able to take the temp up really high with the fan strong even outside (through 2nd crack into burnt roast territory in ~8 mins), so this is a problem with my rig rather than the popper components. I think the main issue is not having the plastic housing to protect from ambient temperature and wind. I could put this back on, or maybe lag the heater and chamber.
The other option might be to increase the input bean mass. I might try this first since there’s plenty of fan power to move the greens around.
Anyway, here’s a picture of the beans at the end.
I bought a severin a couple of weeks ago (recommended by a quite a few people on the uk coffeeforums site) for 25 quid to get back into roasting coffee. Unmodded it roasts ok, though very quick. E.g. First crack was somewhere between 3 and 4 mins. At this sort of rate it is pretty uneven and I had to roast to almost 2nd crack to get something that looked consistent.
I found this instructables piece which gives a lot of detail on controlling it with an arduino and TC4+ shield. This is quite a lot of work, but I realised you can do this stuff in stages and get something usable / drinkable at each stage:
- Unmodified popcorn maker, stopwatch, watch for yellow, listen for cracks
- thermocouple in the chamber
- dimmer on the AC lead, simultaneously reduce fan+heat in tandem to slow roasting speed
- separate the DC motor and AC heating element, control manually
- hook up to TC4, artisan, record roasts
- PID control etc
To get to (3) I bought the following which worked out well:
The cheap voltage dimmer is pretty scary. I made sure the casing was grounded, but I’m still using rubber gloves to move the dial! It was worth doing though, I was able to get a roast to stretch out to longer using the dimmer to vary the temperature climb.
Redber Colombia Excelso Huila, 100g green, 84g out Yellowing was about 160c 2 mins in, first crack at 200c 9 mins 30, dumped at 210c 17.5 mins
‘bean’ temp curve:
The beans looked quite a bit more even out of first crack than with the unmodded popper. Still a bit uneven in the early stages though.
Next step is to get control of the dc motor and heating element separately. I’m hoping that having the fan up high and the heat low in the early stages will allow for a more even roast.
On Saturday I had a long chat with Nick C about the Rust Language Server RFC, and about Racer and how it could all fit together. I wanted to write it up here, mainly to get things straight in my head.
After reading the RFC I thought I had a good idea of how it was going to work, but it turned out I was wrong in a number of places and the scope was larger than I had envisaged.
As I understand the plans correctly, the Rust language server (RLS) will be:
- A compiler - a replacement for the rustc command, for IDEs
- The IDE will use RLS rather than rustc to build software binaries
- RLS will include rustc internally and will also ship with all the rust standard libraries
- The IDE will coordinate actually building the software, and tell RLS what to build (in the same way cargo does for rustc); It could get information from cargo to do this
- A service API for answering low-level semantic queries
- e.g. queries like ‘tell me the fields+methods of <expression>’, ‘which traits are implemented by the type of this expression?’, ‘which call-sites refer to this function?’
- The IDE will then take these lower-level results and build functionality to perform suggestions, searches and refactorings
- A long running process (or a linked library, but long-running)
- RLS will manage an online internal database of semantic information, for a whole project (project is a potentually large collection of crates)
- A crates.io project (or at least that’s the intention). N.B. Building RLS will involve bootstrapping the whole compiler and building the rust libraries, so it will probably also be shipped as binaries in the same way rustc and the stdlibs are.
So in the RLS world the IDE performs both the build coordination, and generates code-completions, suggestions and performs code-transformations (e.g. refactorings). It uses the RLS as a sort of database to retrieve the information it needs to provide these suggestions and transformations. The composition of the RLS API is speculative at the moment.
(N.B. actually all of this is very early and speculative at the moment, so details will likely change in the coming weeks/months as stuff gets built)
Racer + Rustc
I personally hadn’t anticipated that RLS would be used to actually build the rust software. Before the proposal for RLS my intention for racer+rustc was:
client would use their current rustc + cargo build tools to build the project
- racer would link to the same version of rustc as the version being used to compile.
- This would allow racer+rustc to reuse the build artifacts from the built project (which are unstable and change between rustc versions)
rustc would provide a small library interface (which I’ll call ‘librustc’ here in a hand-wavy way)
librustc would have an interface for extracting analysis information at 3 levels of granularity and performance: * Whole crate analysis, taking many seconds to generate per crate * Whole crate ‘skeleton’ analysis, no function bodies, taking ~1 second to generate * Item analysis, taking ~100ms to generate
- The analysis information would be in the dxr ‘save-analysis’ style: - a set of expression spans with reference and type information (record oriented data) - not an ast or a graph
I was intending that the whole-crate analysis would be performed in batch mode on library crates (e.g. from crates.io), and records written to disk. The skeleton analysis would be performed periodically on the currently edited crate as the structure changed, and Item analysis would run continually as you type in response to completion suggestion requests.
The two approaches differ thus:
The focus of ‘librustc’ (hand-wavy-edition) is to export the analysis data. It doesn’t provide any indexing or querying
The focus of RLS is as a code database. The interface to the outside world is a query interface rather than the analysis data.
‘librustc’ (hand-wavy-edition) expects the client (racer in this case, or an ide) to
My main concern with the RLS approach is that it is top-down and big-bang. This is fine, but leaves racer in limbo for a bit. It will be a while before RLS can answer queries that
In an RLS world, racer’s role (as the ide interface for editors) changes a little: - It needs to coordinate builds - It performs completion queries by generating
- A compiler - a replacement for the rustc command, for IDEs
Racer progress has been a bit slow of late, mainly because the implementation is at a crossroads and I wasn’t sure how things would pan out with the IDE tools RFC. Racer 1.1.0 was released yesterday, so I thought it was time for an update
A bunch of Racer-related things happened in the last few months:
Nick Cameron authored an rfc exploring some ideas about adding Rust support for IDEs
I wrote some code to explore using Rustc for type inference and completions.
Some awesome people contributed bugfixes, performance improvements and features to Racer
Thanks to everybody that contributed code and patches since the v1.0 version:
Antoine Kalmbach, Bjørnar Grip Fjær, bluss, Chris Tetreault, Christopher Serr, David Harks, Fredrik Bergroth, Georg Brandl, Ivan Fraixedes, Ivo Wetzel, Jake Goulding, Jakko Sikkar, Jędrzej, João Felipe Santos, Joe Wilm, Kevin K, LFalch, Loïc Damien, Marcell Pardavi, Matt Wismer, Shougo Matsushita, Syohei YOSHIDA, Jonesey13, Vadim Chugunov, Wilfred Hughes, Yamakaky
@jwilm and @birkenfeld deserve a special mention: @birkenfeld for overhauling the source loading/caching and making things considerably faster, and @jwilm for putting in a bunch of work to get Racer working as part of a long running process/daemon.
Racer and the ide tools RFC
The gyst of the ide tools rfc proposal was to create a daemon that would manage the resolution and type information generated by the compiler. It would then present an interface for IDEs to perform queries about the code being written, similar in style to Go’s ‘oracle’ tool.
The idea was that the compiler would generate this information as a separate step, and then the oracle would load, cache and serve this information. The approach is well suited to finished crates, where Rustc can generate a complete static model of the code. It is less straightforward to see how this model should tackle unfinished code in the midst of being written. The idea was that there would be a separate ‘quick-check’ compilation phase, which would generate incremental (partial) information for the oracle to load.
The challenge with a Rustc quick-check compilation is that it would need to be invoked frequently (maybe on each keypress), and be fast enough to provide information required for completions at ide-speeds (in the 100-200ms ballpark). In addition it would also need to be able to cope with unfinished and incomplete code. Rustc isn’t currently designed with these requirements in mind.
I spent some of my spare time prototyping a Rustc typechecker to figure out if we could maybe make Rustc fast enough for quick-check to be feasible. Over this period I learned a bunch about the compiler and I feel I am quite close to having something workable. Initially I was planning to replace Racer’s internal type checking machinery with this code, but unfortunately there are a number of things that make this difficult to roll out.
Basically, despite its flaws, Racer has the following desirable properties that a Rustc based completer would lack:
- Racer doesn’t share internal binary data with Rustc, and so doesn’t need to target a specific version of the compiler
- Racer has low expectations about its input, and happily attempts to complete broken and unfinished code
- Racer works across crates and on sourcecode that hasn’t been compiled.
Replacing Racer’s internal inference machinery with a Rustc based engine would degrade Racer’s current capability in a number of areas and make it much more brittle wrt source code input:
- There would need to a be a Racer version targeted at each version of Rustc (stable, beta, nightlies). Users would need to be savvy enough to download the version that matched their compiler and would need to upgrade their Rustc version if they wanted to pick up new Racer features.
- Users would need to run a precompile step on every crate they wanted to navigate through, to generate the appropriate dependency compile artifacts (rlibs). They’d need to re-run this step whenever they upgraded rust + Racer.
- Racer would fail whenever there was an error that Rustc couldn’t handle. Over time I hope we could get better at this, but at least initially we’d need to come up with ways to educate users on how to keep their code in a shape that could be analysed.
Despite this I still remain convinced that a Rustc Racer is the way to go. (The alternative would be to build out Racer’s type inference to mirror Rustc’s. No small effort!)
I think the best approach in the short term is to use Rustc to augment Racer’s existing functionality and type inference machinery rather than replace it. My plan is to build a separate Rustc based typeck daemon that Racer could invoke to perform additional type checking when its own machinery fails.
This daemon will need to be an optional plugin for Racer since compiling it will be onerous, at least initially while we figure out the best way to package Rustc targeted code. Hopefully this will help us gain experience packaging and using Rustc’s internals in an interactive environment.
(Following up on my comment on the rust IDE rfc)
Here are some performance numbers for a type inference prototype (demoed here), which uses the rustc libraries to generate resolution and type inference information. It runs from scratch against the source code and rlibs with nothing pre-indexed/loaded, and prints the type of the expression highlighted by the cursor.
As I mentioned in the comment, it speeds up the parsing and analysis stages by selectively stripping function bodies as the text is loaded by rustc, replacing them with whitespace and newlines so that the coordinates are the same. I cribbed the code to do this from racer, which has some fast routines for iterating over logical blocks of rust source.
As a target I analysed the rustc_typeck crate.
On my laptop (2ghz x64 ubuntu):
- Phase 1 (parse input): ~50ms (includes stripping the function body text prior to ast parsing)
- Phase 2 (expand) : ~132ms
- Phase 3 all other checks expect body typeck: ~390ms
- Body typeck for single function: - ~19ms
The results suggest to me in this environment we could get a type-signature level index of this crate achieved in ~ half a second, and then perform the body typecheck on the current local scope after each keypress. We would then need to perform an additional step using the type-signature index to deduce the completion results.
The steps for a type-based completion query ( e.g.
- deduce the type of the expression to be completed (i.e. the stuff before the dot)
- generate suggestions by resolving the type and finding e.g. methods, fields, trait impls
Regardless of the level of prior cached indexing, we potentially need to perform the type step (1) on each keypress. This means we should aim to get rustc answering this question in < 100ms (hopefully much less). I suspect this will require some custom compiler work in addition to the current incremental compilation effort.
We could theoretically do (2) without an index directly from the sourcecode and rlibs, (racer currently attempts to do this from source). However this is complicated especially in the face of macros. A rustc generated index would make this simpler and faster, and the index only needs to be at the type-signature level to perform this specific query (i.e. we don’t need information about the contents of functions except for the local scope we are currently searching from).
With the field search occurring outside of rustc we can also be more relaxed about consistency and versioning - e.g. we could perform the search on an old index if we don’t have the most up-to-date one generated yet, since updates outside the current scope will be limited.
I’ve been providing some feedback on an RFC Nick Cameron is writing about providing rust compiler support for IDEs. In order to gain some understanding I’ve been experimenting with a prototype tool to perform type analysis on a crate as fast as possible (as you type).
I wanted to demo this tool and racer to Nick, but unfortunately getting experimental code to compile with rustc is error-prone due to rustc being a fast-moving target. Instead I made a couple of videos to show Nick what I was up to. I thought they might be interesting to other people so with his permission I’m posting them here:
https://www.youtube.com/watch?v=3-f67VHGgVs (~10 mins)
(I’m not very good at speaking on videos so these are a bit mumbly, sorry!)
As I see them, the big upcoming challenges from the perspective of code completion are:
- Creating a useful stable interface to rustc that can be used to extract semantic type and lifetime information
- Performing rustc analysis on incomplete crates (so that the results can be used for providing completions + suggestions as you type)
- Performing the (incremental) analysis with sub-100ms latencies
I’m very excited about the prospect of rustc-enabled perfect completions, and am also eager to work on other functionality for racer (I use intellij daily at work and really miss the ‘extract-method’ refactoring when writing rust code).
I made a Racer v1.0.0 release!
This doesn’t represent any particular milestone in Racer’s development, but rather I wanted to draw a line in the sand before Racer embarks on future architectural changes. A bunch of people have made editor + ide plugins that rely on Racer’s under-specified output protocol and so having a release to point at will help a bit with plugin compatibility. I am planning to stick to semver versioning so it is likely there will be a v1.0.1 soon!
For the future I would like to see Racer mature into a fully fledged library and binary for supporting IDE authors. In addition to better completions, Racer has potentual to provide support for dynamic type-checking, code assistance and refactoring. To deliver this functionality Racer will need to work much closer with rustc and, due to the overheads in running queries through the compiler infrastructure, will likely need to operate as a long running process/library, holding state and caching information. Unfortunately it is also likely that future Racer versions may only build with rust-nightly due to a tighter coupling with unstable rustc internals.
Racer is an evening/weekends spare time project for me, and I am very grateful to everyone who helped out submitting patches, bugfixes and suggestions over the last year. In particular, thanks to:
Andrew Andkjar, Antoine Kalmbach, awdavies, Ben Batha, Björn Steinbrink, Björn Zeutzheim, byronyi, Chris Morgan, Corey Farwell, Dabo Ross, Damien R, Darin Morrison, Derek Chiang (Enchi Jiang), Eike Hein, Emil Lauridsen, Henrik Johansson, Heorhi Valakhanovich, inrustwetrust, Jake Kerr, Jakko Sikkar, Johann Tuffe, Jorge Aparicio, Justin Harper, Keiichiro Ui, Kirill Khazan, krzat, Leonids Maslovs, lilydjwg, Marius, Marvel Mathew, mathieu _alkama_ m, Mattias Bengtsson, Michael Gehring, Michael Maurizi, nlordell, oakes, Pyry Kontio, Renato Zannon, rhysd, Ricardo Martins, Ronald Kinard, rsw0x, Saurabh Rawat, Sebastian Thiel, Shougo Matsushita, Siddharth Bhat, Tamir Duberstein, Tom Jakubowski, Victor-Nicolae Savu, Vincent Huang, Vlad Svoka, Wilfred Hughes, Yamakaky, yasushi abe, Zbigniew Siciarz, Ziad Hatahet
I’ve worked on a bunch of java code bases over the years, and one feature I like about the java environment is that you can obtain a stack trace identifying the throw site of any exception raised and a some good information about the path the code took to get there.
This is really valuable in a production system where a subtle environment change triggers an unexpected violation of an invariant. The violation is unexpected and so the error bubbles all the way up to the generic error handling. Luckily because of the stacktrace you still have context from which to quickly diagnose the error and decide on an appropriate course of action.
Rust currently supports printing a stack trace to stderr when a thread panic!()s, which is really handy. It is enabled by setting the “RUST_BACKTRACE” environment variable. Panics however are too heavyweight to use for most errors in rust code because they offer limited recovery options, and instead it is generally preferred to return std::result::Result.
(Aside: I suspect in the long run panics will all-but disappear from rust. My guess is that some nice sugar will come along for Result types that will make e.g. array indexing with Results palatable (maybe something like ‘?’ syntax), and then panics will become relegated to a small corner of the language, used only for out-of-memory errors and maybe stack overflows.)
So that leaves a problem: How do I get a stacktrace out of an unexpected rust error in production code? The best approach I’ve come up with so far is to combine a custom error with rt::backtrace. Here’s the recipe:
- Create a custom error type
- Add a 'new()' fn. On creation insert a backtrace into the error using rt::backtrace
- For other error types used by the program, convert them using the convert::From
- Create a Result type alias
Now step 3. ensures that when try!() converts an error from e.g. std::io::Error into MyError ...
... then the backtrace is created with this callsite. Unfortunately the backtrace won't point you to where the original std::io::Error was created, but at least you'll have an idea how connect_to_something() came to be called and the fact that the error originated in the TcpStream::connect. If you build the binary with dwarf debuginfo (rustc -g) then you'll even have a file and line number.
The downside to this approach is that std::rt::backtrace::write() is unstable, so only works with nightly rust. Also it sounds like the backtrace functionality will be moved out to a separate crate at some point.
Over the last few weeks I've been spending quite a bit of my evening + weekend spare time in rustc trying out ideas and writing code to figure out what's possible and reasonable for real time type inference.
The goal is to get racer to perform perfect rustc type-inferred completion queries in the 100ms ballpark. To give an idea what racer is up against: using rustc to do a type check pass on a sizeable library (I've using the 'rustc_typeck' crate as an example) takes close to 10 seconds, with 8 seconds of that being the actual type checking.
Here's a representative breakdown of a prototype running against rustc_typeck (times are in seconds):
0.182s parse crate text from files into one big AST 0.711s expand macros, insert std::prelude 0.123s read external crates (libraries) 0.164s resolve crate (i.e. resolve all the static paths to point to actual nodes) 0.046s create a type context object (mk_ctxt()) 0.036s collect the types of all the items (fns, traits etc..) 0.010s infer variance 0.018s coherence check 8.077s perform a full type inference pass
Originally I was hoping I could find a way to cache a type-checked ast + side tables and then just incrementally fill in functions and re-typecheck only them. Unfortunately this approach conflicts heavily with the current rustc design, which freezes the ast shortly after the macro expansion stage (the 2nd phase above). I suspect changing the rustc design to accomodate modifying the ast after this stage would be a large and invasive change.
So if racer can't cache data after the expand stage, the other potentual avenue is to try and speed up the rest of the phases by typechecking less code. 'rustc' still needs a fully cohesive crate to compile, but there are some transformations that can be made to the code that will still leave a cohesive crate.
Rust has the nice charactistic that a function signature is enough to type-check a caller without the analysing the body, so the first obvious transformation is to remove function bodies except for the one you want to type check.
Here's some timings running a type-check over the same crate, but this time with all the function bodies removed:
0.031s parse 0.127s expand 0.085s read crates 0.032s resolve crate 0.002s mk_ctxt 0.016s collect_item_types 0.001s infer variance 0.013s coherence check 0.644s type check
This is closer, although still way-off the 100ms goal. A sub-second typecheck is a pretty good start though. There are other transformations racer can try - e.g. pruning private declarations that can't be reached from the target code.
So if we take this approach, racer's ultimate performance will depend on its ability to prune the ast. To get an idea of what the performance could be like if racer became really good at this I pruned an ast manually. This is rust_typeck with the body of one function intact (I used 'typeck::check::check_bounds_are_used'), and only the direct dependencies of this function. I cheated a bit and used rustc to tell me what the dependencies should be; The timings assume the initial ast was parsed, expanded and cached, and doesn't include any time racer would take to prune the tree.
0.045s re-expand the ast based on 0.000s ast map 0.073s read crates 0.008s resolve crate 0.000s mk_ctxt 0.000s collect_item_types 0.000s infer variance 0.003s coherence check 0.149s type check
So there we are. Still almost a couple of hundred millis off the goal, but definitely in right ballpark. I haven't dug much into the actual rustc typechecking code proper yet, so I'm hoping I can get some further (maybe radical) gains there.
I think the next stage for me is to build a prototype that can do interactive type checking of a crate. Something like 'highlight an expression and it tells you the type'. I hope that by building that I'll learn more about rustc and maybe discover some further optimizations or maybe a better approach.
The big blocker for this release was rustc's libsyntax. I'd like to say a big thanks to Erick Tryzelaar for porting libsyntax into the syntex_syntax crate, and for working hard to remove unstable features from libsyntax. Also I'd like to say a special thanks to Tamir Duberstein for helping to remove the dependency on rust unstable features.
And of course thanks to all the Racer contributors.
(BTW, a side effect of relying on a separate syntax crate is that build times for Racer have gone up dramatically. This is a shame, but I think a small price to pay for Rust 1.0 compatibility)
subscribe via RSS