From: Sam Moore Date: Mon, 28 Oct 2013 07:48:35 +0000 (+0800) Subject: Merge branch 'master' of github:szmoore/MCTX3420 into report X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=f4f4c762db8c7cb2934bb03ae85063e453d68169;hp=45065c1b02650ff08b7a0f4fbca7acd1ac449480;p=matches%2FMCTX3420.git Merge branch 'master' of github:szmoore/MCTX3420 into report Conflicts: server/fastcgi.c server/parameters Also... calibrated sensors --- diff --git a/irc/log b/irc/log index ae6a317..3faa7f5 100644 --- a/irc/log +++ b/irc/log @@ -5345,3 +5345,1160 @@ 22:00 < jtanx> and you can hide the navigation bar 22:00 < jtanx> for more space 22:11 -!- jtanx [~asfa@220-253-133-241.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +--- Day changed Sat Oct 19 2013 +07:59 -!- jtanx [~asfa@220-253-133-241.dyn.iinet.net.au] has joined #mctxuwa_softdev +10:39 < jtanx> blegh, now to start on the js for the control page +11:10 < jtanx> what would you say if instead of all this chdir business +11:10 < jtanx> one of the initial options to start the software is to specify where you want experiments to be held +11:11 < jtanx> e.g /usr/share/experiments or whatever +11:11 < jtanx> then you build the path based on the username +11:25 < sam_moore> That sounds good +11:26 < jtanx> Okay, I'll try to do that then +14:37 < sam_moore> There's some kind of error in mctx.gui.js now +14:37 < jtanx> what's happening? +14:37 < sam_moore> A syntax error +14:37 < jtanx> let me check +14:38 < jtanx> what page is this on? +14:38 < sam_moore> Line 272 (mctx.gui.js): outdiv[0].scrollHeight - outdiv.height() +14:38 < sam_moore> Caused by loading graph.html +14:38 < jtanx> ahhhh +14:38 < jtanx> I know why +14:38 < jtanx> maybe +14:39 < jtanx> I decided to just to $("#errorlog").seterrorlog in mctx.gui.js +14:39 < jtanx> so I don't have to repeat that on every page +14:39 < sam_moore> The debug function also causes some error in chromium +14:39 < jtanx> but if the page deosn't have an error log +14:39 < jtanx> hahaha +14:39 < jtanx> chromium +14:39 < jtanx> what does it say +14:39 < sam_moore> Didn't I tell you that something was causing iceweasel to segfault? I have no choice! :( +14:40 < jtanx> :P +14:40 < sam_moore> console.log.apply(this, arguments); causes an uncaught type error: illegal invocation +14:40 < sam_moore> I shall replace it with "alert" +14:40 < jtanx> hmm +14:41 < jtanx> it's because chrome has a different implementation of console.log +14:41 < jtanx> http://stackoverflow.com/questions/9521921/why-does-console-log-apply-throw-an-illegal-invocation-error +14:41 < jtanx> for that bug +14:41 < sam_moore> Right +14:42 < sam_moore> Why is the call to outdiv.scrollTop on line 272 spread over 3 lines? +14:42 < sam_moore> ... That's probably why it syntax errors +14:42 < jtanx> nah +14:42 < jtanx> it's legit +14:43 < jtanx> the problem is there's no div there if you don't define #errorlog somewhere in your html +14:43 < sam_moore> I thought the html was supposed to load any elements specific to that page +14:44 < sam_moore> Which should avoid this sort of thing happening +14:44 < jtanx> Yeah, except that we were repeating the same sort of load code +14:44 < jtanx> in most of the pages +14:44 < jtanx> I've just added a check in seterrorlog +14:44 < jtanx> which should ignore if there's no such div anyway +14:44 < sam_moore> ok +14:44 < jtanx> that whole 'experiment dir' thing is ~.~ +14:44 < jtanx> I think it's mostly done +14:45 < sam_moore> Ok +14:45 < jtanx> so +14:45 < jtanx> instead of having a file 'name.exp' +14:45 < sam_moore> Yeah that was hacky +14:45 < jtanx> it's a folder 'name.exp' +14:45 < jtanx> all the stuff for one experiment gets stuck in that folder +14:45 < sam_moore> That's slightly less hacky +14:45 < jtanx> so you have something like +14:45 < sam_moore> Makes sense +14:46 < jtanx> /experiments_folder/username/experiment_name.exp +14:46 < sam_moore> Cool +14:46 < sam_moore> If it ever is changed to use local user accounts you can just set experiments_folder to /home +14:47 < jtanx> yeah +14:47 < jtanx> experiments_folder is just an argument you pass to it when you start it +14:47 < jtanx> -e experiment_folder +14:49 < sam_moore> Found a syntax error in graph.html now +14:49 < jtanx> just pushing stuff now +14:49 < jtanx> but what's the errro? +14:50 < sam_moore> There's an unterminated bracket on the runBeforeLoad.done +14:50 < sam_moore> I think +14:50 < jtanx> ah +14:50 < sam_moore> So many brackets +14:50 < jtanx> hehehe +14:50 < jtanx> yeah you're probably right +14:50 < jtanx> netbeans is giving angry red on the bracket +14:51 < jtanx> do you want me to push the fix for that? +14:51 < sam_moore> Well there are other issues with the page +14:52 < sam_moore> No method "always" where it's being chained on the document.ready +14:53 < jtanx> no wait +14:53 < sam_moore> Wait what the hell is it meant to do there +14:53 < jtanx> [14:52:32.186] ReferenceError: mctx is not defined @ http://localhost:8383/MCTXWeb/static/mctx.graph.js:8 +14:53 < jtanx> hmm +14:53 < jtanx> I broke something +14:53 < sam_moore> That's not the problem I'm getting, but generally there are lots of wierd things here +14:53 < jtanx> yeah +14:53 < jtanx> sorry +14:53 < jtanx> it was probably working before I pushed changes yesterday +14:54 < sam_moore> You have runBeforeLoad.done() calling $(document).ready which does nothing, but is chained to .always which will then call $(document).ready which then loads $("#graph-controls").setDevices() +14:55 < sam_moore> ... I think what I originally had was runBeforeLoad.always() calling $("#graph-controls").setDevices +14:55 < sam_moore> Possibly inside a document.ready +14:55 < sam_moore> But now mctx.gui.js is calling document.ready itself... +14:55 < jtanx> waiit +14:55 < jtanx> I think it's ok now +14:56 < jtanx> it was just brackets +14:56 < jtanx> I'll try it on my server first +14:58 < jtanx> yeah, I see what you mean +15:01 < jtanx> ahh +15:01 < sam_moore> Well I fixed it enough to test stuff +15:01 < jtanx> yeah +15:02 < jtanx> you planning on modifying stuff? +15:02 < sam_moore> I think it's just a matter of having runBeforeLoad().done() call $(document).ready() and then initialise stuff +15:02 < jtanx> I'll just keep it in my repo for now +15:03 < sam_moore> With runBeforeLoad.fail if you want to handle bad stuff +15:03 < jtanx> I just made it +15:03 < jtanx> runBeforeLoad().always(function() { +15:03 < jtanx> $(document).ready(function() { +15:03 < jtanx> $("#graph-controls").setDevices(); +15:03 < jtanx> }); +15:03 < jtanx> }); +15:03 < sam_moore> Yep, that's what I just did +15:03 < sam_moore> You can commit it and I'll pull it +15:03 < jtanx> okay +15:03 < sam_moore> If you fix the console and the errorlog things as well +15:03 < jtanx> yep +15:03 < jtanx> pushed that too +15:05 < sam_moore> Hmm, start_time = -1 is not actually giving the most recent second of data +15:05 < sam_moore> That might be a server API problem +15:05 < jtanx> hmmm +15:05 < sam_moore> But it might also explain why things were shitting themselves so much with the fast sampling rate +15:06 < jtanx> what is it giving? +15:06 < sam_moore> Because the jQuery would be getting hundreds of thousands of points... +15:07 < sam_moore> At the moment the server API is not giving any points and giving back a stupid value for start_time +15:07 < sam_moore> I think I might have broken it when I changed all the clocks +15:07 < jtanx> when did you change ti? +15:07 < sam_moore> Just now +15:07 < jtanx> ah +15:07 < sam_moore> I'm hoping that my first assumption about the thousands of points was the case before I broke it as well :P +15:08 < sam_moore> (ie: It was already broken before I broke it more) +15:08 < jtanx> ahahahah +15:08 < jtanx> I can try +15:08 < jtanx> I still have the old version +15:09 < jtanx> with start_time=-1 it returns nothing +15:09 < jtanx> start_time in the response is constant +15:11 < sam_moore> Yeah I get wierd stuff +15:11 < sam_moore> start_time = 17889.590701 +15:12 < sam_moore> "current_time" : 18589.539255 +15:12 < jtanx> is that with clock_gettime? +15:12 < sam_moore> "running_time" : 699.948554, +15:12 < sam_moore> Yes +15:13 < sam_moore> Oh herdurp +15:13 < sam_moore> start_time in the JSON response is *not* the same as start_time the parameter to the sensor +15:13 < jtanx> yeah +15:13 < sam_moore> .... should probably change that +15:13 < jtanx> start time of experiment +15:14 < jtanx> experiment_start_time? +15:14 < jtanx> long variable names... +15:14 < sam_moore> I think it's actually the program start time +15:14 < jtanx> nup +15:14 < jtanx> experiment starttime +15:14 < sam_moore> Alright +15:14 < jtanx> oh +15:14 < jtanx> actually you may be right +15:14 < sam_moore> Haha +15:14 < jtanx> I thought i changed it +15:14 < jtanx> wway back when i did the control stuff +15:15 < jtanx> initially +15:15 < sam_moore> ControlData has a start_time variable +15:15 < jtanx> yeah +15:15 < sam_moore> But g_options has a start_time variable +15:15 < sam_moore> -_- +15:15 < jtanx> that's confusing +15:15 < jtanx> hahaha +15:15 < sam_moore> Yeah, what idiot designed this... +15:15 < jtanx> ~.~` +15:19 < sam_moore> Right, sensor values are being saved relative to Control_GetStartTime (experiment starting time) +15:19 < sam_moore> That's fine +15:19 < jtanx> yep +15:19 < sam_moore> Sensor_Handler gets "current_time" from that as well +15:19 < jtanx> Thread safety on that is dodgey +15:19 < jtanx> I must say +15:20 < jtanx> but it's probably fine +15:20 < sam_moore> It's fine, clock_gettime is posix thread safe +15:21 < sam_moore> Oh, do you mean if the experiment changes +15:21 < jtanx> yeah +15:21 < jtanx> the whole *Control_GetStartTime() looks dodgy too +15:22 < sam_moore> Perhaps just a "double Control_CurrentTime()" would be better +15:22 < sam_moore> But it works +15:22 < jtanx> hmm +15:24 < sam_moore> Anyway, I think start_time=-1 is not giving any data because the default sampling rate is 1s and therefore current_time - 1 is outside the data range most of the time +15:25 < sam_moore> start_time=-3 gives a single point with the default sampling rate +15:25 < sam_moore> Increasing the sampling rate then you appear to get the right points +15:31 < sam_moore> I'm going to repeat my timestamp test with the current software under regular kernel and RT linux +15:32 < sam_moore> I know we can't ever get RT linux but I want to see if it makes much of a difference +15:46 < jtanx> okay +15:50 < jtanx> finally... back to actually coding the control page +22:59 -!- jtanx [~asfa@220-253-133-241.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +--- Day changed Sun Oct 20 2013 +15:56 -!- Callum [~Callum@106-68-161-33.dyn.iinet.net.au] has joined #mctxuwa_softdev +15:59 < Callum> hey sam, got a link to the dropbox? +16:06 < sam_moore> https://www.dropbox.com/sh/km90dmbdrgin3mg/OimGp0qopv +16:06 < Callum> thanks +16:07 < sam_moore> I think I might be able to hack together a user management system based on UserCake +16:08 < sam_moore> Someone else will have to make it look pretty though +17:25 < Callum> opencv is fucking retarded. +17:25 < Callum> C++ has a bunch of different functions for each individual type of blur +17:25 < Callum> C has one function but you pass it an ID for which type of blur you want to use.. +17:30 < sam_moore> OpenCV used to be in C, but they moved it to C++ +17:30 < sam_moore> But it still supports the C API +17:30 < sam_moore> Through some wierd magic +17:30 < sam_moore> At least, if you run our program in valgrind, it reports a lot of stuff happening with things like "std::string" and "cv::Mat"; ie: C++ namespaces +17:32 < Callum> yea. its a bit of a bitch to code in C...all the documentation is for c++ pretty much +17:32 < Callum> like right now im trying to find out how to simply set all elements to 0.. where in c++ its just Mat::Scalar(0) +17:32 < sam_moore> If you really want you can probably compile in C++, but I'm not sure if it will cause issues elsewhere +17:33 < Callum> il just keep going like this. just a couple of things atm i need to figure out. hopefully thats it +17:33 < sam_moore> Cool +17:34 < sam_moore> The server now supports 3 entirely different authentication methods :S +17:34 < Callum> haha. a little excessive. +17:34 < Callum> hopefully they'll be happy with atleast one of them.. +17:34 < sam_moore> If we modify UserCake we can run our own user management system +17:35 < sam_moore> I need to change it so that instead of users registering themselves it's the admin that adds users manually though +17:35 < sam_moore> Also we'd have to get email working +17:57 < Callum> uuh. so using one of his test images for the microscope, running it through Canny gives a bunch of squiggly lines all over the place +17:57 < Callum> its funny, the part which is actually the edge...has the most black +17:57 < Callum> il try some of his other images and see what it does +20:22 -!- Callum [~Callum@106-68-161-33.dyn.iinet.net.au] has quit [EOF From client] +20:50 -!- MctxBot_ [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +21:05 -!- MctxBot [~twang@220-253-133-241.dyn.iinet.net.au] has quit [Ping timeout] +21:06 -!- Irssi: #mctxuwa_softdev: Total of 2 nicks [0 ops, 0 halfops, 0 voices, 2 normal] +21:08 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +21:10 < jtanx> working on the report the day before... +21:10 < jtanx> what is this madness +22:23 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +--- Day changed Mon Oct 21 2013 +07:40 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +08:04 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +09:15 -!- jtanx [~asfa@130.95.131.138] has joined #mctxuwa_softdev +11:00 -!- jtanx [~asfa@130.95.131.138] has quit [Ping timeout] +11:03 -!- jtanx_ [~asfa@130.95.54.13] has joined #mctxuwa_softdev +11:03 -!- jtanx_ is now known as jtanx +11:22 < jtanx> mctxserv[10834]: FATAL: Data_Save (data.c:80) - Error seeking to end of DataFile test/sensor_1 - File too large +11:23 < jtanx> jeremy@pickle:~/git/MCTX3420/server/test$ du -sh * +11:23 < jtanx> 0 actuator_0 +11:23 < jtanx> 2.0G sensor_0 +11:23 < jtanx> 2.1G sensor_1 +11:23 < jtanx> after running overnight +11:23 < jtanx> hahaha +11:36 < sam_moore> The 32G SD card might have been a good thing to buy... +11:36 < sam_moore> Just in case +11:36 < jtanx> I tried removing the line i made to fstab +11:36 < jtanx> still didn't work +11:36 < sam_moore> Damn +11:36 < jtanx> was there anything important on it +11:36 < jtanx> could just flash it with the internal memory +11:37 < sam_moore> I'd be very hesitant to flash it to the internal memory if it doesn't boot +11:37 < sam_moore> There may be something else wrong with it +11:37 < jtanx> I mean +11:37 < jtanx> there's a script +11:37 < jtanx> to copy the internal memory to the sd card +11:37 < sam_moore> Oh, that's cool +11:37 < sam_moore> We can do that +11:37 < jtanx> Okay +11:37 < sam_moore> Since Ubuntu magically solved our image problems, we should use it +11:38 < jtanx> yeah, that was weird +11:38 < jtanx> just as long as there's nothing important on the sd card +11:38 < sam_moore> Probably change the phrasing in the report from "We don't know why this fixed it" to "We fixed it through our 1337 haxor skills" +11:38 < jtanx> >.> +11:39 < jtanx> how did you set up usercake? +11:39 < jtanx> like where's this db-settings.php that it mentions +11:39 < jtanx> oh right +11:39 < jtanx> never mind +11:41 < sam_moore> It comes with an install directory that I removed +11:41 < sam_moore> We should add that because we might want to modify the database structure +11:41 < jtanx> yeah +11:41 < sam_moore> Eg: It's a real pain that user permission levels are in a seperate table to everything else +11:41 < sam_moore> And doesn't make sense... +11:41 < jtanx> normalised database design +11:41 < jtanx> :P +11:42 < sam_moore> We can go with the "strcmp(user, "admin")" for now +11:42 < sam_moore> Did you get the email about LEFT vs RIGHT can? +11:42 < jtanx> yeah +11:42 < sam_moore> I think I should ask +11:42 < jtanx> pneumatics wants left +11:43 < sam_moore> "Are you looking at the box from the FRONT or BACK" +11:43 < jtanx> everyone else thought right +11:43 < jtanx> hahahaah +11:43 < sam_moore> "Also, which side is the FRONT" +11:43 < jtanx> the case team got blasted this morning +11:43 < sam_moore> Uh oh +11:43 < jtanx> apparentl +11:43 < jtanx> y +11:43 < sam_moore> Then again, we all get blasted every week +11:43 < jtanx> true +11:43 < sam_moore> I think we manage to get the least blasting, but still +11:43 < jtanx> oh yeah +11:44 < jtanx> apparently the report IS individual +11:44 < jtanx> per group +11:44 < jtanx> I'm getting confused +11:44 < sam_moore> Haha +11:44 < jtanx> and it's due next monday??? +11:44 < sam_moore> -_- +11:44 < jtanx> confusion over the due date +11:44 < jtanx> adam apparently didn't know, and thought sparkplus would send out the date +11:44 < jtanx> i don't know what sparkplus has got to do with this though +11:45 < sam_moore> How long does it have to be? +11:45 < jtanx> I have no idea +11:45 < sam_moore> Sparkplus is for our peer evaluation +11:45 < jtanx> yeah +11:45 < jtanx> but the relation to report due date? +11:45 < sam_moore> Pretty lazy if you ask me, and I don't care that they might read this (frankly I doubt it) to get the students to mark each other +11:46 < jtanx> Yeah +11:46 < jtanx> urgh +11:46 < jtanx> getting 1045 (access denied) from mysql +11:46 < jtanx> pretty sure I've got the pwd right +11:46 < sam_moore> It might be what happens in the Real World (TM), but this is not some company, this is university, my grades should not depend on someone else +11:47 < sam_moore> Try with `mysql` from the command line and check the password? +11:50 < jtanx> herp derp +11:50 < jtanx> no password +11:53 < jtanx> now that I think about it, it's almost exactly like the django idea, except in php +11:53 < sam_moore> Yep +11:53 < jtanx> when you install it, is there any default login? +11:54 < sam_moore> No; you register an account and that account becomes the admin +11:54 < sam_moore> (of course you can also manually update the database) +11:54 < jtanx> okay +11:54 < sam_moore> You could change the install script to do that +11:54 < jtanx> how are usernames and passwords stored in the db? +11:54 < jtanx> crypt? +11:54 < jtanx> (do you specify the algorithm?) +11:55 < sam_moore> passwords are crypted with SHA6 +11:55 < sam_moore> The other stuff is plain text +11:55 < sam_moore> UserCake default is SHA1 or something +11:55 < jtanx> sha6? +11:55 < sam_moore> I changed it to SHA6 +11:55 < jtanx> does it use something called pcks +11:55 < jtanx> pcsk2 +11:55 < jtanx> pcks2* +11:56 < sam_moore> Wait... by SHA6 I mean "SHA-Whatever-you-get-with-$6$-in-the-salt" +11:56 < sam_moore> Which is actually SHA-512 +11:56 < sam_moore> Derp +11:56 < jtanx> hmm +11:57 < sam_moore> I couldn't find any references to security issues with doing it this way, but that doesn't mean there aren't any +11:57 < sam_moore> However there's always going to be some security issues with anything we use +11:57 < jtanx> yeah +11:57 < jtanx> about the stuff in login.c +11:57 < jtanx> you'll definitely want to change tat +11:57 < jtanx> "SELECT password FROM %s WHERE user_name = \"%s\";", +11:58 < jtanx> sql injection right there +11:58 < sam_moore> I know +11:58 < sam_moore> That's why Login_Handler removes all non alpha-numeric characters from the user name +11:58 < jtanx> the password? +11:58 < sam_moore> The password isn't part of the MySQL query +11:58 < sam_moore> Only the user name +11:58 < jtanx> oh right +11:58 < sam_moore> So the password can contain any characters +11:59 < jtanx> yeah +11:59 < jtanx> I know with sqlite you can use placeholders +11:59 < jtanx> then you 'bind' stuff to those placeholders +11:59 < sam_moore> I think you can with MySQL too +11:59 < jtanx> probably +11:59 < sam_moore> Feel free to change it if you want, I figured it was safe if username was already checked for bad characters +12:00 < jtanx> yep +12:03 < jtanx> Pneumatics and electronics wants to test the system this wednesday, starting from 10am +12:03 < sam_moore> That provides a convenient reason to not work on ENSC1001 +12:03 < sam_moore> So I'll be there +12:03 < jtanx> Hahaha +12:08 < jtanx> nneded php5-gd +12:08 < jtanx> these username/password restrictions are attrocious +12:57 < sam_moore> The UserCake ones? +12:57 < jtanx> yeah +12:57 < sam_moore> Also, sorry I'll be late today +12:57 < jtanx> that's ok +12:58 < sam_moore> We're getting there... +12:59 < sam_moore> The question is, do they want a finished project, or do they want a report on an unfinished project +12:59 < jtanx> hahaha +12:59 < sam_moore> Because we'll have to devote a lot of time to the report +12:59 < jtanx> yes +13:00 < sam_moore> We've looked into every layer of software from linux kernel drivers to databases and human computer interaction... +13:01 < jtanx> o.o +13:01 < sam_moore> I think we can conclude that they are all horrible hacks tied together with shoelaces +13:01 < jtanx> about usercake +13:02 < jtanx> is it just +13:02 < jtanx> you have a set of pages +13:02 < jtanx> and they're either accessible or not +13:02 < jtanx> to the user? +13:02 < sam_moore> Yep, and I know you're thinking we could put the API as one of those pages :P +13:02 < jtanx> haha +13:02 < sam_moore> But I think it's better to keep the API seperate from user management +13:02 < jtanx> didn't think that +13:02 < jtanx> but was just trying to understand usercake +13:02 < sam_moore> Fair enough +13:02 < jtanx> but since everything's in php +13:03 < jtanx> we can do some templating +13:03 < jtanx> instead of hacking it together with javascript +13:03 < sam_moore> Sure +13:03 < sam_moore> The main modification I wanted to make was a page that lets the admin upload a list of users and get rid of the registration page (or restrict it somehow) +13:04 < jtanx> yeah +13:04 < jtanx> but some things are better done in php +13:04 < jtanx> like that whole 'load the sidebar in jaascript' is crap +13:04 < jtanx> and the whole 'if not logged in, redirect the user with javascript' too +13:04 < sam_moore> Feel free to add php to the main gui, it is probably a good choice +13:05 < jtanx> okay +13:05 < jtanx> once I figure out how stuff works +13:05 < sam_moore> I'm just not experienced enough with this sort of web development stuff +13:05 < jtanx> I can probably say it's a learing experience for everyone +13:05 < sam_moore> Yes +13:06 < sam_moore> I am pretty happy with how we've done this, even if it doesn't get finished +13:06 < jtanx> Yeah, it's quite ok +13:06 < sam_moore> I think we made good design choices +13:06 < sam_moore> Probably the best one was when you convinced me to use FastCGI instead of writing a custom HTTP server :P +13:07 < sam_moore> Or we'd be well and truly screwed when all the requirements for security and user management came along +13:07 < jtanx> hahaha +13:07 < jtanx> php +13:07 < jtanx> custom webserver +13:07 < jtanx> well +13:07 < jtanx> it might have worked +13:08 < jtanx> call php5-cli with system +13:08 < jtanx> hahaha +13:08 < sam_moore> I could probably do something like that with enough time +13:08 < sam_moore> But it wouldn't really be worth it +13:14 < jtanx> oh right +13:14 < jtanx> you changed usercake to use crypt +13:15 < jtanx> I'll change the password for the sd card image back to what we used before +13:16 < jtanx> username will be ubuntu though +13:18 < sam_moore> ok +13:18 < jtanx> the internal memory still uses temppwd though +13:20 < jtanx> if we use usercake we'd have to use php throughout the stuff anyway +13:20 < jtanx> unless you want it to be publicly visible +--- Log closed Mon Oct 21 14:11:57 2013 +--- Log opened Mon Oct 21 14:40:20 2013 +14:40 -!- sam_moore [matches@motsugo.ucc.gu.uwa.edu.au] has joined #mctxuwa_softdev +14:40 -!- Irssi: #mctxuwa_softdev: Total of 2 nicks [0 ops, 0 halfops, 0 voices, 2 normal] +14:40 -!- Irssi: Join to #mctxuwa_softdev was synced in 3 secs +14:43 -!- jtanx [~asfa@130.95.54.13] has joined #mctxuwa_softdev +--- Log closed Mon Oct 21 14:49:35 2013 +--- Log opened Mon Oct 21 15:08:16 2013 +15:08 -!- sam_moore [matches@motsugo.ucc.gu.uwa.edu.au] has joined #mctxuwa_softdev +15:08 -!- Irssi: #mctxuwa_softdev: Total of 1 nicks [0 ops, 0 halfops, 0 voices, 1 normal] +15:08 -!- Irssi: Join to #mctxuwa_softdev was synced in 2 secs +15:17 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +15:22 -!- jtanx_ [~asfa@130.95.54.13] has joined #mctxuwa_softdev +15:22 -!- jtanx_ is now known as jtanx +15:22 -!- jtanx [~asfa@130.95.54.13] has quit [EOF From client] +18:17 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +18:36 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +22:14 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +22:57 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +--- Day changed Tue Oct 22 2013 +09:35 < sam_moore> So... looking at our system diagram, we have "mains electricity" going directly into the BeagleBone +09:35 < sam_moore> That should probably be fixed +09:38 < sam_moore> Dammit isn't there an svg version somewhere +10:06 < sam_moore> Oliver said Adrian's goal was to "teach us a lesson" +10:07 < sam_moore> If the goal is to turn us into cynical bastards, I was already a cynical bastard before this unit, so I fail to see the point +10:07 < sam_moore> ... Talking to myself, excellent sign +10:38 -!- jtanx [825ffcb6@ircip4.mibbit.com] has joined #mctxuwa_softdev +10:39 < sam_moore> Hi Jeremy +10:40 < sam_moore> Are you OK with the section I listed for you? +10:40 < jtanx> Sure, it seems okay +10:41 < jtanx> This is going to be a busy week +10:42 < sam_moore> I basically told my ENSC1001 team that if I have to choose between failing two units, I will choose to fail ENSC1001 +10:42 < sam_moore> I am now "that guy" +10:43 < jtanx> Urgh :( +10:44 < sam_moore> The requirements for the diary in terms of software are a bit ridiculous +10:44 < sam_moore> In the 21st century +10:44 < sam_moore> You'd think we could use git +10:44 < sam_moore> As opposed to writing down source code in our diary +10:44 < sam_moore> ... writing down code... +10:44 < sam_moore> By hand +10:44 < jtanx> Yeah, the requirements for this whole unit have been quite a stretch\ +10:44 < sam_moore> In a diary +10:44 < sam_moore> What the hell??? +10:44 < jtanx> I asked during the meeting if he wanted a printed copy of the code :P +10:45 < sam_moore> One idea I have had that would have been good +10:45 < sam_moore> Is to always make 2 git commits whenever you work on something +10:45 < sam_moore> One when you start +10:45 < sam_moore> And one when you finish +10:45 < jtanx> good idea +10:46 < sam_moore> So, does he want a printed copy of the code? +10:52 < jtanx> nah +10:58 < sam_moore> Damn, that might have been fun to hand in +10:58 < jtanx> We could do it just for shits +10:58 < jtanx> print out the doxygen manual too +10:58 < sam_moore> Hahaha +10:58 < sam_moore> The doxygen manual is probably bigger than the printed source code +10:59 < jtanx> Get a quote from uniprint +10:59 < jtanx> print and bind it +10:59 < sam_moore> Sure +10:59 < sam_moore> It won't help though +10:59 < jtanx> yeah +10:59 < sam_moore> The response to anything that looks like it was a large amount of work +10:59 < sam_moore> is "you overcomplicated it!" +10:59 < jtanx> ~.~ +11:02 < jtanx> okay, i'm leaving uni +11:02 -!- jtanx [825ffcb6@ircip4.mibbit.com] has quit ["http://www.mibbit.com ajax IRC Client"] +11:31 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +11:37 < jtanx> well, I guess now's a good time than any to learn LaTeX +11:39 < sam_moore> Haha +11:39 < sam_moore> You don't have to learn LaTeX +11:39 < sam_moore> It's just, for a 30 page report +11:39 < sam_moore> If I am the one collating it +11:39 < sam_moore> I want to use LaTeX +11:39 < sam_moore> Hang on, I'll submit something in a minute, I got sidetracked looking at flow chart applications +11:41 < jtanx> that's ok +11:41 < jtanx> I've always wanted to learn latex anyway +11:43 < sam_moore> If you're good at programming you can pick up latex pretty easily +11:43 < jtanx> yeah, it doesn't look to bad +11:43 < sam_moore> The scary bit is all the preamble stuff +11:43 < sam_moore> Which you just copy/paste from examples unless you actually want to change something majorly +11:43 < jtanx> haha +11:43 < sam_moore> The rest is just like any markup language, except a bit more verbose +11:44 < sam_moore> I have a nice template structure from my thesis we can use +11:44 < jtanx> Cool +11:57 < sam_moore> https://github.com/szmoore/MCTX3420/tree/report/reports/final +11:58 < sam_moore> Essentially every chapter gets a single .tex file in the chapters directory +11:58 < sam_moore> report.tex determines the formatting and stuff, so the chapter.tex files don't have to have anything other than content +11:58 < jtanx> Okay +11:59 < sam_moore> I'm not sure if it builds under windows easily +11:59 < jtanx> well +11:59 < jtanx> one of the reasons why i hesitated to use latex was because it's something like a few gb to isntall +12:00 < jtanx> it should build under windows fine +12:00 < sam_moore> Ah +12:00 < sam_moore> I think it's worth it +12:00 < jtanx> I have no disk space to install it on my laptop +12:00 < sam_moore> For something of this scale... using a standard word processor... +12:00 < sam_moore> Haha +12:00 < jtanx> but it's installed on my server +12:00 < sam_moore> Just worry about the content, I can make it compile either way +12:00 < jtanx> yeah +12:01 < sam_moore> Also, do you know any good flow chart stuff? Other than omnigraffle which is OSX only +12:01 < jtanx> ms visio +12:01 < jtanx> defacto standard +12:02 < jtanx> never particularly liked it though +12:03 < jtanx> it's free of msdnaa, but of course you need a windows computer... +12:03 < sam_moore> Yeah, that will ruin my productivity +12:04 < sam_moore> I do have windows, but rebooting is not fun, installing windows equivelants of all the software I use under linux is not fun, wine is probably broken, and running my windows operating system off the hard disk as a VM can be done, but gives temperamental results +12:07 < jtanx> hahaha +12:15 < sam_moore> Hmm, I have LibreOffice draw +12:15 < sam_moore> Maybe that doesn't suck +12:16 < jtanx> I hate using Libre/Open* +12:16 < jtanx> never formats properly +12:17 < sam_moore> Yeah... just trying to join two shapes together nicely is taking more than 3 seconds, screw that +12:17 < jtanx> hahaha +12:17 < sam_moore> I'll try this "Dia" thing +12:18 < sam_moore> Oh dear +12:18 < sam_moore> There's "Dia" +12:18 < sam_moore> And "Diagramly" +12:20 < jtanx> what about graphviz +12:28 < sam_moore> giffly looks good +12:28 < jtanx> Any particular name for our server software? +12:28 < sam_moore> Oh god, it needs a name +12:28 < jtanx> I've just been referring to it as 'Server API' +12:28 < sam_moore> Yeah :S +12:29 < sam_moore> I have a friend who didn't name their software and had to give a report on it and the only thing anyone commented on was its lack of name +12:29 < sam_moore> So the next time... +12:30 < jtanx> >.> +12:30 < sam_moore> He said "This part of the software is 'sabertooth' and this part is 'timberwolf'" +12:30 < sam_moore> And they liked it +12:30 < jtanx> Hahaha +12:30 < jtanx> what +12:31 < sam_moore> ... +12:31 < sam_moore> What's the greek god of disaster? +12:31 < sam_moore> Or exploding pressurised vessels +12:32 < sam_moore> Demeter, goddess of failure... +12:33 < sam_moore> Just stick with Server API for now +12:35 < sam_moore> I think I'll use gliffy even though it requires me to sell my soul to whatever company owns it +12:35 < sam_moore> Can't be worse than facebook +12:35 < jtanx> ._. +12:35 < jtanx> fair enough +12:36 < sam_moore> For a "vector graphics" editor, "dia" just looks horrible +12:36 < sam_moore> The lines don't alias properly at all +12:36 < jtanx> does it generate svg output +12:37 < sam_moore> gliffy does and I'm not looking back :P +12:37 < jtanx> hahaha +12:42 < sam_moore> gliffy is really nice +12:42 < jtanx> which flow chart are you doing +12:42 < sam_moore> At the moment, the sensors thread +12:43 < jtanx> Okay +12:43 < jtanx> ahh +12:43 < jtanx> gliffy would have been handy for uml crap I had to do +12:46 < sam_moore> If you want to start with flow charts, try and do one for how ajax goes from the client and then data comes back through the labyrinth of FastCGI calls :P +12:47 < sam_moore> I have a feeling they will consider flow charts to be "documentation" and not "report" though :S +12:47 < jtanx> We can put it in the report +12:47 < jtanx> then write about it +12:47 < sam_moore> Haha +12:47 < sam_moore> I actually have some results from experiments which they keep going on about in the marking key +12:48 < sam_moore> I'm not sure what we can do for the results of tests from the GUI +12:48 < jtanx> yeah +12:48 < sam_moore> At one point I had a graph in flot that was plotting the time it took to update the graph in flot +12:48 < sam_moore> But I never saved it +12:48 < jtanx> o.O +12:49 < sam_moore> Something that might be nice is a cachegrind profile +12:49 < sam_moore> Whenever I do a report on software I try and profile it somehow +12:49 < sam_moore> Anyway... the focus has shifted from "make the system work" to "write a report" +12:49 < jtanx> Yeah +12:50 < sam_moore> So we're pretty much just going to leave the system in an almost finished state, but since it would take someone a few weeks to learn it, they'll just start again... +12:50 < sam_moore> and fail in all the same ways next year :( +12:50 < jtanx> Hahahaha, of course +13:48 < sam_moore> This flow chart stuff is really annoying +13:48 < sam_moore> How much detail needs to be included +13:48 < sam_moore> Argh +13:48 < jtanx> I just made one in visio +13:48 < jtanx> high level crap +13:49 < jtanx> but still annoying to make +13:49 < jtanx> I was hoping to make the flow chart +13:49 < jtanx> then comment on that +13:49 < jtanx> in the text +13:49 < sam_moore> Ok +13:50 < jtanx> Do you think something like this is ok?http://i.imgur.com/acNVXME.png +13:52 < sam_moore> That looks good +13:52 < sam_moore> It's completely inconsistent with what my sensor flow chart looks like visually though +13:52 < jtanx> :S +13:53 < sam_moore> Should we try make them all look the same, or not bother? +13:53 < jtanx> Hmm +13:53 < sam_moore> I'm not sure if this works; can you access it like this: https://www.gliffy.com/go/html5/5007621?app=1b5094b0-6042-11e2-bcfd-0800200c9a66 +13:53 < jtanx> I have to login +13:54 < jtanx> presumably your login +13:54 < sam_moore> Yeah, ok, I'll just put it under figures +13:55 < sam_moore> https://github.com/szmoore/MCTX3420/blob/report/reports/final/figures/sensor_thread.png +13:57 < jtanx> Nice +14:00 < sam_moore> Whatever, don't worry about consistency, just make the charts +14:00 < jtanx> If we get time, maybe then +14:24 < sam_moore> We don't have time, I just spent 6 hours making one diagram :S +14:24 < sam_moore> Ok, only 4.5 hours so far, but still +14:24 < jtanx> :S +14:24 < jtanx> I've spent what, 30 mins deciding how to write this one paragraph +14:25 < jtanx> This is ridiculous +14:25 < sam_moore> Were you there when Oliver said that Adrian's goal with this unit was to punish us? +14:25 < jtanx> Hahaha +14:25 < jtanx> Nah I wasn't there +14:26 < sam_moore> The whole point is apparently to make us realise that we need a project manager +14:26 < sam_moore> Something that I'm pretty sure I could work out for myself thankyou very much +14:26 < jtanx> >.o +14:29 < sam_moore> I've pushed some sort of report layout thing +14:32 < sam_moore> It looks like most of the marks are on the "Approach" section +14:32 < sam_moore> Which reminds me, I need to finish adding up my hours... +14:33 < sam_moore> For cost calculation we could go "We have an average of X lines per day. According to James Trevelyn, the metric is 15 lines per day. Assuming this means 8 hours full time work at $150 an hour... we need this much money." +14:34 < jtanx> Yeah +16:09 < sam_moore> Ok, I'm starting to get the hang of this +16:09 < sam_moore> If I just don't sleep for the next week +16:09 < sam_moore> We'll be fine +16:09 < jtanx> :S +16:10 < sam_moore> Dammit are we still testing this system on Wednesday? +16:10 < jtanx> I think so +16:10 < jtanx> I'm trying to push out as much of this as possible today, but I'm not getting that far... +16:10 < sam_moore> Ok, it should be fine, we just need some minor changes +16:10 < sam_moore> Like... actually putting all the sensors/actuators in the software... +16:10 < sam_moore> All the logic to control them is there though +16:10 < jtanx> Yeah, maybe... +16:11 < sam_moore> Don't worry about shit like the microphone in the graph page; the data can be recorded and downloaded, that's good enough for it +16:11 < jtanx> Yep +16:12 < jtanx> I don't particularly see the use of the microphone anyway +16:12 < sam_moore> I don't know either +16:13 < sam_moore> I realised that doing something like PID control with the current sensor/actuator logic would be a pain... +16:13 < jtanx> Will we need pid control? +16:14 < sam_moore> I don't think so +16:14 < jtanx> I hope not +16:14 < sam_moore> Well... not if the pressure regulator is good +16:14 < jtanx> Here's to hoping +16:14 < jtanx> (and if electronics gets their PWM-to-analogue thing going) +16:15 < sam_moore> At least it's sort of possible to make PID control work, if you can identify the sensor somehow in Actuator_Loop and then just look at (sensor->current_data.value)/(sensor->points_read) +16:15 < sam_moore> (Because I was lazy and reused current_data to store the sum of the points read before averaging, rather than the actual most recent point) +16:16 < sam_moore> ergh, we'll fix that if it needs fixing anyway +16:23 < sam_moore> Actually, if the goal is just to plot pressure vs strain, we don't care exactly what the pressure regulator is *supposed* to be... we just blindly increase it and then plot the strain vs an actual pressure sensor instead +16:24 < jtanx> That's true +16:26 < sam_moore> I wonder how harsh they will be on the "you must have designed the system completely before you implement it" +16:26 < sam_moore> Do we have to determine what every line of code is going to be before we write them? +16:27 < jtanx> Well there's no way that can be the case +16:28 < jtanx> If that were the case i think we'd still be stuck trying to write the basics +16:28 < sam_moore> At a high level our design is still exactly what is on that block diagram from ages ago +16:28 < sam_moore> At a lower level... it's been through a few changes +16:53 < sam_moore> ... I'm at 13 pages, but a lot of them are blank +16:55 < jtanx> Ideally it should be ~5 pages/person since there's 6 people... +16:55 < jtanx> How it actually turns out, well... +16:57 < sam_moore> Well +16:57 < sam_moore> One flow chart == 1 page... +16:57 < sam_moore> I'm pretty sure I need to make at least 3 flow charts... +16:57 < jtanx> Hahaha +16:58 < sam_moore> Hmmm, could we hand in 29 pages of flow chart and 1 page of text... +16:58 < sam_moore> The "teamwork" process could be a fun flowchart to make +16:58 < sam_moore> Put lots of skull and crossbones on it +16:58 < jtanx> :P +16:59 < sam_moore> I'm pretty sure if you just count the figures I wanted to include that would make more than 5 pages +16:59 < sam_moore> Perhaps this is achievable then +17:00 < sam_moore> At least, getting 30 pages is achievable, not sure about meeting the marking criteria +17:00 < jtanx> Yeah +17:09 < jtanx> I'm at about 2 pages, and I've barely even started on explaining anything +17:09 < jtanx> just reasoning why we chose what we chose +17:28 < jtanx> "So Adrian just made an LMS announcement and the report is now due on Friday Week 13 (5pm), which is slightly more reasonable." +17:28 < jtanx> (Justin just emailed) +17:56 < sam_moore> I wonder if James' refreshing new GUI will be ready to show off by Tuesday +17:56 < jtanx> Hehe +17:57 < jtanx> Hopefully +17:58 < sam_moore> LMS has two reports... +17:58 < sam_moore> "Individual Report" and "Final Report" +17:58 < jtanx> really +17:58 < jtanx> why +17:59 < sam_moore> Maybe the individual report is for the peer assessment mark? +17:59 < jtanx> ah +17:59 < jtanx> the marks on lms are depressing me +18:00 < sam_moore> The progress report marks? +18:00 < sam_moore> They are pretty depressing +18:00 < jtanx> the quiz marks and soldering lab +18:00 < sam_moore> Oh those +18:00 < sam_moore> They are even more depressing +18:01 < sam_moore> Wait, this maths doesn't make sense +18:01 < sam_moore> The meetings are worth 52/1010 +18:01 < sam_moore> The tutorials are worth 808 +18:01 < sam_moore> The soldering video is 100 +18:01 < jtanx> it's lms being screwy +18:01 < sam_moore> And our final report is worth 23? +18:02 < jtanx> the quiz marks and tutorial is marked outof 100% +18:02 < jtanx> I don't know what the others are marked out of +18:02 < jtanx> (well tutes out of 4) +18:11 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +19:16 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +20:20 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +20:22 -!- jtanx_ [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +20:22 -!- jtanx_ is now known as jtanx +21:14 < sam_moore> I just did a test that I probably should have done ages ago, but it confirms my assertion that C is much better (at least in terms of performance) for multithreaded stuff than python +21:14 < jtanx> well yeah +21:14 < jtanx> but then how much performance do you require +21:14 < sam_moore> All the performance +21:15 < jtanx> :P +21:15 < sam_moore> At the fastest possible sampling rate, python is up around 0.01s in terms of the standard deviation of the distribution you get +21:15 < sam_moore> So that's pretty bad +21:18 < sam_moore> That's on a multicore processor actually +21:18 < sam_moore> I guess I could repeat the test on the actual beaglebone, but I think the graph proves the point +21:20 < sam_moore> ... Anyway, they said they wanted results of tests and things... so that's a test we can put in if we need to. +21:21 < jtanx> Yeah +21:24 < jtanx> https://github.com/niklasvh/html2canvas +21:25 < jtanx> oh that's cool +21:41 < sam_moore> Your section of the report is really good +21:41 < jtanx> Really? +21:41 < jtanx> I thought it was really wishy washy +21:41 < sam_moore> Well it justifies the design choice pretty well +21:42 < jtanx> Thanks +21:42 < sam_moore> At one point people were seriously discussing using things other than HTTP +21:43 < jtanx> What could we have used +21:43 < jtanx> designed our own protocol? +21:43 < sam_moore> Yeah, which would have sucked, that's what I had to explain +21:44 < jtanx> Yeah +21:44 < sam_moore> It was something along the lines of "why do we need to do all this complicated web stuff, can't we just have one device talking to another device over some kind of serial cable" +21:44 < jtanx> I could imagine the heartache involved in trying to use some custom protocol +21:44 < jtanx> hahaha +21:44 < sam_moore> In the first place... you then have to build two custom devices as well as write a custom protocol... +21:46 < jtanx> Yeah +21:46 < sam_moore> We did need a raspberry pi or beaglebone, whether or not using an arduino as well for low level stuff would be better I'm not sure +21:47 < jtanx> Hmm +21:47 < sam_moore> I don't think it would have been worth the effort needed to get the RPi/BBB to talk to the arduino +21:47 < jtanx> An arduino may have been better for all the gpio/adc stuff I guess +21:47 < jtanx> but yeah +21:47 < jtanx> the communication between the device and the arduino +21:48 < sam_moore> Well you'd have a request coming in via HTTP, and then that would have to be sent over USB to the arduino to do the appropriate hardware control +21:48 < sam_moore> Which would probably make it difficult to make the sensors/actuators independent of the client +21:49 < jtanx> Well, there's also the option of i2c +21:49 < jtanx> but yes +21:49 < jtanx> an extra layer of communication +21:49 < jtanx> :/ +21:49 < sam_moore> Either way, you have to store the data somehow... and you don't want it to be dependent on the client request... +21:50 < sam_moore> The more I think about it, the less I like it... +21:51 < sam_moore> So I'm glad we went with the BeagleBone even though it's gpio/adc/pwm control is terrible +21:51 < jtanx> Hahaha +21:51 < jtanx> love/hate relationship with the bbb +21:52 < sam_moore> You'd need to have interrupt handlers and some kind of synchronisation going on +21:52 < sam_moore> You still need mutexes and conditionals with threads, but it's a lot easier +21:53 < sam_moore> ... and we'd still need the multithreaded stuff anyway, because we'd still have sensors on the actual RPi/BBB to deal with +21:53 < sam_moore> Yep, it would have been worse +21:53 < jtanx> Yeah +21:54 < sam_moore> We should definitely note this in the report somewhere +21:55 < jtanx> Yeah +21:55 < jtanx> Help justify why we agreed to the bbb +21:55 < sam_moore> Well we did sort of agree to it because of that +21:55 < sam_moore> It was partly "Hmm, we only have to write code for one device" +21:56 < sam_moore> And partly because electronics wanted it to avoid having to build the extra hardware +21:56 < sam_moore> And partly because if electronics wanted it then we wouldn't have to write our own BOM and order things :P +21:56 < jtanx> :P +22:18 < jtanx> MySQL Database BLARGH +22:18 < jtanx> hehe +22:21 < sam_moore> Do my thread flow charts make sense? +22:22 < sam_moore> It's hard to know objectively +22:22 < sam_moore> But I suspect looking at them for too long may cause the brain to shut down +22:24 < sam_moore> Dammit, I can't remember if tomorrow is supposed to be some important assessment thing for ENSC1001 or not +22:24 < jtanx> o.o +22:24 < jtanx> I'm still trying to familiarise myself with the meaning of all the flow chart symbols +22:24 < jtanx> but it seems okay +22:24 < jtanx> don't you have to give a presentation or something for ensc1001 +22:25 < sam_moore> Yeah, but it's alright, that's next week +22:25 < sam_moore> All the things are due next week +22:26 < sam_moore> That's all that matters to me right now, because I want to sleep +22:26 < jtanx> :S +22:26 < sam_moore> Next Tuesday I will hate myself for sleeping this Tuesday +22:26 < sam_moore> But Sam from next Tuesday is a jerk anyway +22:26 < sam_moore> I never liked that guy +22:26 < jtanx> Hey, sleep is important +22:27 < jtanx> One thing about the flow charts, doesn't fatal just call exit(), so no deinitialisation happens +22:27 < sam_moore> Um, sort of +22:27 < sam_moore> But when the program exits, Cleanup gets called +22:27 < sam_moore> Which calls the deinitialisation functions +22:27 < jtanx> When did that get enabled +22:28 < sam_moore> Cleanup has been there for ages +22:28 < sam_moore> It's just been empty most of the time +22:28 < jtanx> Ah +22:28 < sam_moore> Although, I'm not sure if calling exit() is the best way to do it when you have multiple threads +22:28 < jtanx> Meh +22:28 < jtanx> when you have a fatal situation +22:29 < sam_moore> Software isn't going to help? +22:29 < jtanx> no +22:29 < sam_moore> I was planning on having exit called with different error codes +22:29 < jtanx> but what can you do with that +22:30 < sam_moore> You can restart the server using run.sh +22:30 < jtanx> but with the different error codes? +22:30 < sam_moore> Or you can start a quick and dirty program that will make sure everything's deinitialised +22:30 < jtanx> Ah +22:30 < jtanx> I guess that could work +22:30 < sam_moore> One error code for "really bad thing happened, we're going to die" +22:30 < sam_moore> And one for "Something dumb happened, restart the server" +22:31 < jtanx> About the graphs changing colours +22:31 < jtanx> is it okay to just add a legend +22:31 < sam_moore> It is OK to do whatever is the minimal work for the maximum reward +22:31 < jtanx> hahaha +22:31 < jtanx> well +22:32 < jtanx> it was really easy to add the legend +22:32 < sam_moore> Good, problem solved +22:32 < jtanx> :P +22:33 < jtanx> For the actuators +22:33 < jtanx> did you remove that fatal call +22:33 < jtanx> when the value was not safe? +22:33 < sam_moore> I think I did +22:33 < jtanx> Okay +22:33 < sam_moore> It reports an error and doesn't set the actuator +22:33 < sam_moore> And in theory it cancels the remaining steps +22:33 < jtanx> That's good +22:33 < sam_moore> Meaning the actuator just stays at the last safe value +22:35 < sam_moore> Ah, it doesn't cancel the steps, but that can be easily changed +22:35 < sam_moore> But currently it would just continually report the error until all the steps were finished +22:36 < jtanx> either way's fine +22:37 < jtanx> what's the default sample rate +22:38 < sam_moore> It used to be 1s +22:38 < jtanx> it's just that overnight it's generating about 2G of data +22:38 < jtanx> which doesn't sound like 1s +22:39 < sam_moore> Then it most likely isn't anymore +22:39 < sam_moore> Nope, it's now 1e-4 seconds +22:39 < sam_moore> Changed for the microphone test +22:39 < jtanx> ahh +22:40 < sam_moore> Should probably make the starting sample rate an argument to Sensor_Add +22:40 < jtanx> I was wondering why it was consuming 30% cpu on idle +22:40 < jtanx> ls +22:40 < sam_moore> Ergh, if the microphone is running with everything else, it will generate a lot of data +22:41 < jtanx> Yeah +22:41 < sam_moore> Fortunately I added averaging +22:41 < sam_moore> And apparently they just want an average from the microphone +22:41 < sam_moore> Or a "level" +22:41 < sam_moore> Or something +22:41 < jtanx> oO +22:42 < sam_moore> We'll just have the microphone sample at 1e-6 and have 1e6 averages per point +22:42 < sam_moore> (ie: 1 point gets recorded every second) +22:46 < sam_moore> Hmm, or you could make the microphone's ReadFn only succeed if the last pressure reading is higher than a certain value +22:46 < sam_moore> But that's getting complicated +22:49 < sam_moore> Good night anyway +22:52 < jtanx> Okay +22:53 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +23:01 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +23:55 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +--- Day changed Wed Oct 23 2013 +19:30 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +23:01 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +--- Day changed Thu Oct 24 2013 +07:42 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +10:21 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +14:16 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +14:36 -!- Rowan [~Rowan@106-68-210-88.dyn.iinet.net.au] has joined #mctxuwa_softdev +14:47 < jtanx> Hey +15:45 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Connection reset by peer] +15:47 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +17:27 -!- Rowan [~Rowan@106-68-210-88.dyn.iinet.net.au] has quit [EOF From client] +17:51 < jtanx> huh +17:51 < jtanx> it was surprisingly easy to add post support +17:56 < sam_moore> That's cool +17:56 < jtanx> you just fgets from stdin instead +17:56 < jtanx> it's in the same format too +17:56 < sam_moore> Excellent +17:57 < jtanx> we probably do have to unescape html strings though +17:57 < jtanx> like if someone had a comma in their password +17:57 < sam_moore> If we had a progress report we could put "potential gaping security leak plugged" +17:57 < jtanx> Hahaha +17:57 < jtanx> but it still allows the get request version +17:57 < sam_moore> Oh well +17:57 < jtanx> but then that's your own fault +17:57 < sam_moore> Yep +17:59 < sam_moore> Doesn't look like the IRC channel will get a mention in the "communication" section of the report :( +17:59 < sam_moore> Oh no wait, it does +17:59 < sam_moore> Good +17:59 < sam_moore> ... That "overall schedule"... +17:59 < jtanx> Haha +17:59 < sam_moore> I started it, and quickly realised it was a waste of time +18:01 < sam_moore> Also, need to hate on the beaglebone less +18:01 < sam_moore> Otherwise they might try and do something stupid like replace it next year +18:01 < jtanx> That would be stupid +18:01 < sam_moore> Yeah, it was a pain, but probably still one of the best things to use +18:02 < sam_moore> RPi with dedicated ADC/DAC modules would probably have been best +18:02 < sam_moore> Distributed system of arduinos.... noooo +18:02 < jtanx> Yeah +18:02 < jtanx> RPi with ADC/DAC would have been cool +18:02 < jtanx> but only because there's more support for the RPi +18:02 < sam_moore> Yes +19:19 < jtanx> hmm +19:20 < jtanx> maybe I should enable POST only for the login module? +19:20 < jtanx> Right now, if POST and GET data is received, the GET data is discarded in place of the POST data +19:28 < jtanx> Yeah, okay I only enabled it for login +22:38 < jtanx> Hahahahaha +22:39 < jtanx> I passed our code through ohloh - https://www.ohloh.net/p/MCTX3420 +22:39 < jtanx> Apparently it's work 3 years of effort, and is valued at $150k +22:49 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +--- Day changed Fri Oct 25 2013 +08:48 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +09:50 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +09:56 < sam_moore> Um... we didn't make our first commit in 200 +09:56 < sam_moore> * 2000 +09:56 < sam_moore> 10 developers??? +09:57 < sam_moore> There are 6 of us +09:57 < sam_moore> And some people contributed about 2 lines of source code... +09:57 < sam_moore> Mostly written in JavaScript +09:57 < sam_moore> Really? +09:57 < sam_moore> Is that counting the jQuery UI guff? +09:58 < sam_moore> That's annoying, because the C part of the project is like 50% comment lines +09:58 < sam_moore> And it says "low number of comments" based on the JavaScript :S +09:59 < sam_moore> Still... this is really cool +10:00 < jtanx> hahaha +10:00 < jtanx> the 2000 commit was because we made commits on the bbb +10:00 < sam_moore> ok... +10:00 < jtanx> but it lacks an rtc +10:00 < sam_moore> Where do we get the extra 4 developers from? +10:00 < sam_moore> "Debian" is one +10:00 < jtanx> and for some reason the time didn't get updated +10:01 < jtanx> umm +10:01 < jtanx> hmm +10:01 < jtanx> Ubuntu user? +10:01 < sam_moore> Oh, "Callum" and "Callum-" are treated seperately -_- +10:01 < jtanx> oh +10:01 < sam_moore> That's still only 8 +10:02 < jtanx> the results are a bit skewed +10:02 < jtanx> because a lot of the javascript is just from other libraries +10:02 < jtanx> well at least i think so +10:04 < sam_moore> Yeah +10:05 < sam_moore> Is it easy to just pass a subset of the code through? +10:08 < jtanx> Yep - you have to set what's ignored +10:08 < jtanx> I've just set it to ignore all *.min.js files (essentially all external js files) +10:08 < jtanx> But it won't get updated for a while +10:09 < jtanx> I've got to go for ~ 2 hrs +10:10 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +10:12 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +12:46 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +13:42 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +20:44 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +21:20 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +21:36 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +23:39 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +--- Day changed Sat Oct 26 2013 +08:43 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +09:22 -!- justin_kruger [~justinkru@CPE-60-230-240-161.lnse1.wel.bigpond.net.au] has joined #mctxuwa_softdev +09:23 -!- justin_kruger [~justinkru@CPE-60-230-240-161.lnse1.wel.bigpond.net.au] has quit [EOF From client] +09:27 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +09:38 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +13:42 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +15:00 -!- Callum [~Callum@106-68-161-33.dyn.iinet.net.au] has joined #mctxuwa_softdev +15:10 < Callum> just finished the code to get the edge. atm it just returns the position on their object. was thinking we'd just use the timestamp and go this was the additional width at this time, additional width at time +1, so rate of change is 2*difference etc +15:11 < Callum> also pushing 3 images to testing (actually i should go abck and push original too) but to show it blurred, after canny and with the found edge imposed over it (without w.e values i had set at the time..) +15:15 < jtanx> Nice +15:16 < Callum> ok merged +15:17 < Callum> i might need some help merging this into the server code too +15:18 < Callum> just to make sure its all good +15:34 < jtanx> Sure +15:34 < jtanx> I'm really busy right now though with this other crappy project that I have to do +15:35 < jtanx> If not today, then maybe during tomorrow's meeting (if we're having a meeting then?) +15:37 -!- Callum [~Callum@106-68-161-33.dyn.iinet.net.au] has left #mctxuwa_softdev [] +15:38 -!- Callum [~chatzilla@106-68-161-33.dyn.iinet.net.au] has joined #mctxuwa_softdev +15:55 -!- Callum [~chatzilla@106-68-161-33.dyn.iinet.net.au] has quit [Ping timeout] +16:06 -!- Callum [~chatzilla@106-68-161-33.dyn.iinet.net.au] has joined #mctxuwa_softdev +16:07 < Callum> we were meeting tomorrow? also i have an assignment due monday i havent started. was going to try and work through that and work on another project i have due..as well as this +16:07 < Callum> still need to start my writeup for this. +16:40 < Callum> also, if the image is completely black, the read function should return false (havent yet tested this but it should be the case) +16:53 < Callum> also looking at justins diagram, why isnt the microscope not included? +16:54 < Callum> or is it just both under "camera" +17:01 < jtanx> Yeah +17:01 < jtanx> I think it's just under "camera" +17:02 < Callum> ok. +17:02 < jtanx> I dunno, I thought we should try to meet some time next week +17:02 < jtanx> monday's our usual meeting time +17:02 < Callum> you said tomorrow though didnt you? +17:03 < jtanx> Ohh +17:03 < jtanx> sorry +17:03 < jtanx> my bad +17:03 < jtanx> I meant monday +17:03 < Callum> was going to say, my body clock so out of wack its sunday already? +17:03 < Callum> yea. if i can get the assignment done tomorrow il be happy to spend most oif the day trying to finish some of this off +17:03 < jtanx> Haha mine is +17:03 < Callum> to be fair, i normally work saturdays +17:03 < Callum> i took it off to do work. +17:04 < jtanx> :/ +17:04 < Callum> so it should feel like a sunday +17:04 < jtanx> I had this assignment where I thought I had completed it, but when I reread the specs I found out that I missed a large portion of it +17:05 < Callum> ouch +17:05 < Callum> what assignment was this for? +17:05 < jtanx> cits3242 +17:05 < jtanx> some programming unit +17:05 < Callum> figured by the cits. :p +17:05 < jtanx> hehe +17:06 < jtanx> yeah, if it wasn't for that i'd be solely working on this project right now +17:07 < Callum> yea iv kind of been working on 3 or 4 things all at once. and not gotten far in anything. +17:07 < jtanx> yeah, it's always crap when things are due at the same time +17:08 < jtanx> but 3-4 things at once? that's harsh +17:12 < Callum> umm. assignment due tomorrow i havent started. i have anoither group project but we only really have 2 members and wev barely done anything (due friday, meant to have 4/5 members) +17:12 < Callum> physics assignment also due on friday +17:12 < Callum> just trying to figure out how to get it all done +17:13 < Callum> not going to be a fun week. then after this weeks over its major catchup in the 3 units i have exams for +17:13 < Callum> oh and this tute adrian decided to give us...should probably look at that some time too +17:16 < Callum> whats the model for the microscope? cant find anything in dropbox +17:22 < Callum> they are using the microscope only on the non-exploding one im assuming? +17:23 < jtanx> Yeah +17:23 < jtanx> the microscope +17:23 < jtanx> it's a kaiser baas something +17:23 < jtanx> KBA03030 +17:24 < Callum> thanks +17:57 -!- Callum [~chatzilla@106-68-161-33.dyn.iinet.net.au] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"] +22:04 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +23:31 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has left #mctxuwa_softdev [] +--- Day changed Sun Oct 27 2013 +07:59 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +08:46 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +15:08 < sam_moore> Blergh, what are we going to put in the "Results" part of this report? +15:09 < sam_moore> "We plugged it all in and it didn't explode" ? +15:13 < sam_moore> Also it looks like gliffy only lets you export one svg on the trial account +15:14 < sam_moore> And if the image isn't exactly A4 size, pdf is terrible +15:14 < sam_moore> So the remainder of my figures will be png +15:20 < sam_moore> Do we need to find references for all these things that we worked out and/or already knew ourselves... +15:20 < jtanx> haha +15:21 < sam_moore> I feel like referencing git commits +15:21 < jtanx> Yeah I was going to do that +15:21 < sam_moore> We should do that +15:21 < jtanx> results +15:21 < jtanx> it seems to work +15:21 < sam_moore> ... +15:21 < sam_moore> I put in a "Customer Satisfaction" section under results +15:22 < jtanx> :P +15:22 < sam_moore> It says "No matter what we did, the customer was not satisfied" +15:22 < sam_moore> Next year they'll probably be told the beaglebone is stupid and start doing it all on an arduino +15:22 < jtanx> mm probably +15:22 < sam_moore> And then get told that it needs a user management system... +15:23 < jtanx> hahahaha +15:23 < sam_moore> I'm having trouble with what level of assumptions I should make about the reader's knowledge +15:23 < sam_moore> Do they know what a thread is? +15:24 < sam_moore> Do they know what "high level" and "low level" mean +15:24 < jtanx> Thats... what I was having difficulty with too +15:24 < sam_moore> Do they know what a kernel is? +15:24 < jtanx> how far back do you have to explain terminology +15:24 < sam_moore> Or context switching... +15:24 < sam_moore> Argh +15:24 < jtanx> write a treatise on multithreaded computing +15:24 < sam_moore> :S +15:24 < sam_moore> This is rather simple multithreaded stuff really +15:26 < sam_moore> I think we need a "terminology" section +15:27 < jtanx> THat would be good +15:30 < jtanx> oh yeah +15:30 < jtanx> we should be able to use commas again +15:30 < jtanx> for that actuator step thing +15:30 < jtanx> because it decodes the string first +15:31 < jtanx> not sure if that will introduce any security issues though +15:32 < sam_moore> Who cares, ship it +15:33 < sam_moore> But I don't think so? +15:33 < jtanx> :P +15:33 < jtanx> maybe +15:33 < jtanx> I dunno +15:33 < sam_moore> sscanf can at most read to the end of the string +15:34 < jtanx> since it decodes %hh to the hex value +15:34 < jtanx> I mean if you give it a url encoded string +15:34 < jtanx> with %01 +15:34 < jtanx> it would change that to the character 0x01 +15:34 < jtanx> which is like some control code +15:34 < sam_moore> Hmm +15:34 < jtanx> we could limit it so anything less than 0x20 is a space +15:34 < sam_moore> Just do that then +15:35 < jtanx> but it's probably important to url decode strings +15:35 < jtanx> what if someone had a , in their password +15:35 < jtanx> or weird characters +15:35 < sam_moore> God dammit +15:35 < sam_moore> call isprint() ? +15:35 < jtanx> nah I mean +15:36 < jtanx> it should be fine if we convert anything < 0x20 to a space +15:36 < jtanx> but it's better that we're url decoding stuff +15:36 < sam_moore> Ok +15:36 < jtanx> because if we didn't, the password string would be like %2d instead of - +15:36 < jtanx> for example +15:36 < sam_moore> Yeah, that would suck +15:37 < sam_moore> Do you think Rowan and/or James will write their parts of the report? +15:37 < jtanx> Maybe, I dunno +15:38 < jtanx> Rowan was trying to copy the wiki, which was written mostly by Justin? +15:38 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +15:38 < sam_moore> Yeah, that's not really cool +15:38 < jtanx> :/ +15:38 < sam_moore> I don't think there's much that can be just put into the report from the wiki anyway +15:38 < sam_moore> It's too informal +15:38 < sam_moore> And has a lot of "TODO: Fill this in" pages +15:38 < jtanx> Yeah, they're counted separately anyway +15:39 < jtanx> as in, there's a specific mark for wiki pages +15:39 < sam_moore> Oh dear +15:39 < jtanx> (or equivalent documentation afaik) +15:39 < sam_moore> Every area there's a mark for is just another area they can deduct marks for not liking +15:39 < jtanx> Yeah, I'm not liking it very much +15:40 < sam_moore> Are we supposed to talk about the other subsystems or focus on our own? +15:40 < jtanx> I'm hoping to focus on our own really +15:40 < jtanx> but I guess there would be overlap +15:48 < jtanx> did you see the microscope stuff that callum did +16:00 < sam_moore> I glanced at it +16:00 < sam_moore> It's in the main directory not sensors/ +16:01 < sam_moore> Does it work? +16:01 < jtanx> well from the test pictures it looks quite good +16:01 < jtanx> not sure how well it will work with the real thing +16:02 < sam_moore> Hahaha +16:02 < sam_moore> If it works with what we were given, good enough +16:02 < jtanx> Yep +16:03 < sam_moore> We seem to end up having to actually implement all of sensors things +16:03 < sam_moore> I guess I will add the calibration in at some point before Tuesday +16:04 < jtanx> :/ +16:04 < sam_moore> It shouldn't be that hard, I've done it before +16:04 < sam_moore> Convert raw value to nearest two indexes in an array +16:04 < sam_moore> Interpolate between those two indexes +16:05 < sam_moore> I think for the Microphone there's 1024 points because he tested it with a 10 bit ADC somehow +16:05 < sam_moore> For other things... +16:05 < sam_moore> There are 10 points +16:05 < jtanx> 1024 points +16:05 < jtanx> wait what +16:06 < sam_moore> 1 point per ADC value +16:06 < sam_moore> As in, I was sent a lookup table of ADC value -> dB level +16:06 < jtanx> oh right +16:06 < jtanx> yeah +16:06 < jtanx> he was testing it on an arduino +16:06 < jtanx> was the arduino calibrated properly +16:07 < sam_moore> Lalala not listening... +16:07 < jtanx> :P +16:07 < sam_moore> Oh, it loks like I only have 20 points anyway +16:09 < sam_moore> This calibration does assume that electronics uses exactly the same voltage divider that sensors guy used +16:09 < jtanx> it's probably close enough +16:09 < sam_moore> Well maybe +16:09 < sam_moore> Just trusting the data sheet is probably close enough +16:10 < sam_moore> I'm not sure I see the point in calibrating a device if you don't actually use the final setup to calibrate it... +16:10 < jtanx> Yeah, true +16:22 < sam_moore> Is the use of '*' in pointer names a "standard"... I always thought it was... how you made a variable a pointer +16:22 < jtanx> Oh yeah that +16:31 < sam_moore> God dammit I have no motivation left to do anything +16:32 < jtanx> urgh +16:32 -!- jtanx changed the topic of #mctxuwa_softdev to: :( +16:34 < sam_moore> Should I put your part of the report into tex? +16:34 < jtanx> Yeah, I guess +16:34 < sam_moore> I think if I put your part and Justin's part combined with my hopelessly incomplete part... +16:34 < sam_moore> we will actually have too many pages +16:34 < jtanx> :/ +16:34 < jtanx> was there a hard limit? +16:35 < jtanx> I probably need to cut down my section, too much rambling +16:36 < sam_moore> I think it's fine +16:37 < jtanx> I dunno +16:37 < jtanx> I have the feeling I'm not following the guidelines correctly on what I'm supposed to talk about +16:37 < sam_moore> We can't win either way +16:37 < jtanx> Yeah, like where am I meant to find the references for half the stuff I did +16:38 < sam_moore> Surely James Trevelyn would have got a few papers out of his Telelabs thing? +16:39 < sam_moore> I'll leave it for now +16:39 < jtanx> Okay +16:41 < sam_moore> My section will be great when I actually include all those figures I refer to... +19:33 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has quit [Ping timeout] +21:26 -!- MctxBot [~twang@106-68-93-93.dyn.iinet.net.au] has joined #mctxuwa_softdev +23:24 -!- jtanx [~asfa@106-68-93-93.dyn.iinet.net.au] has quit ["it has been segmented"] diff --git a/reports/final/references/refs.bib b/reports/final/references/refs.bib index 60805d2..852b0a1 100644 --- a/reports/final/references/refs.bib +++ b/reports/final/references/refs.bib @@ -20,4 +20,5 @@ title = "MCTX3420 2013 Git Repository on GitHub", howpublished = "\url{https://github.com/szmoore/MCTX3420}" } + diff --git a/reports/final/teamwork - justin.docx b/reports/final/teamwork - justin.docx new file mode 100644 index 0000000..a27d002 Binary files /dev/null and b/reports/final/teamwork - justin.docx differ diff --git a/server/actuator.c b/server/actuator.c index 1c105db..7473485 100644 --- a/server/actuator.c +++ b/server/actuator.c @@ -35,7 +35,7 @@ int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn a->init = init; // Set init function a->sanity = sanity; - + a->cleanup = cleanup; pthread_mutex_init(&(a->mutex), NULL); if (init != NULL) @@ -51,16 +51,35 @@ int Actuator_Add(const char * name, int user_id, SetFn set, InitFn init, CleanFn /** - * One off initialisation of *all* Actuators + * Initialisation of *all* Actuators */ -#include "actuators/ledtest.h" -#include "actuators/filetest.h" +#include "actuators/pregulator.h" +#include "actuators/relays.h" void Actuator_Init() { //Actuator_Add("ledtest",0, Ledtest_Set, NULL,NULL,NULL); - Actuator_Add("filetest", 0, Filetest_Set, Filetest_Init, Filetest_Cleanup, Filetest_Sanity, 0); + //Actuator_Add("filetest", 0, Filetest_Set, Filetest_Init, Filetest_Cleanup, Filetest_Sanity, 0); + Actuator_Add("pregulator", 0, Pregulator_Set, Pregulator_Init, Pregulator_Cleanup, Pregulator_Sanity, 0); + Actuator_Add("can_select", RELAY_CANSELECT, Relay_Set, Relay_Init, Relay_Cleanup, Relay_Sanity, 0); + Actuator_Add("can_enable", RELAY_CANENABLE, Relay_Set, Relay_Init, Relay_Cleanup, Relay_Sanity, 0); + Actuator_Add("main_pressure", RELAY_MAIN, Relay_Set, Relay_Init, Relay_Cleanup, Relay_Sanity, 1); } +/** + * Deinitialise actuators + */ +void Actuator_Cleanup() +{ + for (int i = 0; i < g_num_actuators; ++i) + { + Actuator * a = g_actuators+i; + if (a->cleanup != NULL) + a->cleanup(a->user_id); + } + g_num_actuators = 0; +} + + /** * Sets the actuator to the desired mode. No checks are * done to see if setting to the desired mode will conflict with @@ -143,8 +162,12 @@ void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg) */ void Actuator_SetModeAll(ControlModes mode, void * arg) { + if (mode == CONTROL_START) + Actuator_Init(); for (int i = 0; i < g_num_actuators; i++) Actuator_SetMode(&g_actuators[i], mode, arg); + if (mode == CONTROL_STOP) + Actuator_Cleanup(); } /** @@ -173,7 +196,7 @@ void * Actuator_Loop(void * arg) // Currently does discrete steps after specified time intervals struct timespec wait; - DOUBLE_TO_TIMEVAL(a->control.stepsize, &wait); + DOUBLE_TO_TIMEVAL(a->control.stepwait, &wait); while (!a->control_changed && a->control.steps > 0 && a->activated) { clock_nanosleep(CLOCK_MONOTONIC, 0, &wait, NULL); @@ -381,7 +404,7 @@ void Actuator_Handler(FCGIContext * context, char * params) ActuatorControl c = {0.0, 0.0, 0.0, 0}; // Need to set default values (since we don't require them all) // sscanf returns the number of fields successfully read... - int n = sscanf(set, "%lf,%lf,%lf,%d", &(c.start), &(c.stepwait), &(c.stepsize), &(c.steps)); // Set provided values in order + int n = sscanf(set, "%lf_%lf_%lf_%d", &(c.start), &(c.stepwait), &(c.stepsize), &(c.steps)); // Set provided values in order if (n != 4) { // If the user doesn't provide all 4 values, the Actuator will get set *once* using the first of the provided values @@ -434,3 +457,9 @@ Actuator * Actuator_Identify(const char * name) } return NULL; } + +DataPoint Actuator_LastData(int id) +{ + Actuator * a = &(g_actuators[id]); + return a->last_setting; +} diff --git a/server/actuator.h b/server/actuator.h index f818526..46c2f44 100644 --- a/server/actuator.h +++ b/server/actuator.h @@ -67,13 +67,14 @@ typedef struct /** Sanity check function **/ SanityFn sanity; /** Cleanup function **/ - CleanFn clean; + CleanFn cleanup; /** Last setting **/ DataPoint last_setting; } Actuator; extern void Actuator_Init(); // One off initialisation of *all* Actuators +extern void Actuator_Cleanup(); extern void Actuator_SetModeAll(ControlModes mode, void *arg); extern void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg); @@ -85,6 +86,7 @@ extern Actuator * Actuator_Identify(const char * str); // Identify a Sensor from extern void Actuator_Handler(FCGIContext *context, char * params); // Handle a FCGI request for Actuator control extern const char * Actuator_GetName(int id); +extern DataPoint Actuator_LastData(int id); #endif //_ACTUATOR_H diff --git a/server/actuators/Makefile b/server/actuators/Makefile index a2ba5dc..dfc247c 100644 --- a/server/actuators/Makefile +++ b/server/actuators/Makefile @@ -3,7 +3,7 @@ CXX = gcc FLAGS = -std=c99 -Wall -pedantic -g -I../ #-I/usr/include/opencv -I/usr/include/opencv2/highgui For OpenCV LIB = -lpthread -OBJ = ledtest.o filetest.o +OBJ = relays.o pregulator.o HEADERS = $(wildcard *.h) RM = rm -f diff --git a/server/actuators/pregulator.c b/server/actuators/pregulator.c new file mode 100644 index 0000000..1cd62d3 --- /dev/null +++ b/server/actuators/pregulator.c @@ -0,0 +1,45 @@ +#include "pregulator.h" +#include "../bbb_pin.h" + + +#include "../data.h" +#define PREGULATOR_PWM ECAP0 +#define PREGULATOR_PERIOD 500000 +//16666667 + +/** PWM duty cycles raw **/ +static double pwm_raw[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6}; +/** Calibrated pressure values match with pwm_raw **/ +static double preg_cal[] = {96, 190, 285, 380, 475, 569}; + +/** + * Initiliase the pressure regulator + */ +bool Pregulator_Init(const char * name, int id) +{ + return PWM_Export(PREGULATOR_PWM) && PWM_Set(PREGULATOR_PWM, false, PREGULATOR_PERIOD, 0); +} + +bool Pregulator_Cleanup(int id) +{ + if (!PWM_Set(PREGULATOR_PWM, false, PREGULATOR_PERIOD, 0)) + return false; + PWM_Unexport(PREGULATOR_PWM); + return true; +} + +bool Pregulator_Set(int id, double value) +{ + double anti_calibrated = Data_Calibrate(value, preg_cal, pwm_raw, sizeof(pwm_raw)/sizeof(double)); + if (anti_calibrated < 0) + anti_calibrated = 0; + if (anti_calibrated > 1) + anti_calibrated = 1; + return PWM_Set(PREGULATOR_PWM, false, PREGULATOR_PERIOD, anti_calibrated*(PREGULATOR_PERIOD)); +} + +bool Pregulator_Sanity(int id, double value) +{ + return (value >= 0 && value < 570); +} + diff --git a/server/actuators/pregulator.h b/server/actuators/pregulator.h new file mode 100644 index 0000000..c721140 --- /dev/null +++ b/server/actuators/pregulator.h @@ -0,0 +1,11 @@ +#ifndef _PREGULATOR + +#include "../common.h" + +extern bool Pregulator_Init(const char * name, int id); +extern bool Pregulator_Cleanup(int id); +extern bool Pregulator_Set(int id, double value); +extern bool Pregulator_Sanity(int id, double value); + + +#endif //_PREGULATOR diff --git a/server/actuators/relays.c b/server/actuators/relays.c new file mode 100644 index 0000000..dfa3d74 --- /dev/null +++ b/server/actuators/relays.c @@ -0,0 +1,48 @@ +#include "relays.h" +#include "../log.h" + +#include "../bbb_pin.h" + + +static int GetGPIO(int id) +{ + switch (id) + { + case RELAY_CANSELECT: + return 14; + case RELAY_CANENABLE: + return 115; + case RELAY_MAIN: + return 112; + } + Fatal("Unknown id %d", id); + return 0; +} + +bool Relay_Init(const char * name, int id) +{ + if (!GPIO_Export(GetGPIO(id))) + return false; + return GPIO_Set(GetGPIO(id), false); +} + +bool Relay_Cleanup(int id) +{ + bool err = GPIO_Set(GetGPIO(id), false); + GPIO_Unexport(GetGPIO(id)); + return err; +} + +bool Relay_Set(int id, double value) +{ + bool set = (bool)value; + return GPIO_Set(GetGPIO(id), set); +} + +bool Relay_Sanity(int id, double value) +{ + //bool set = (bool)value; + //TODO: Make a more sane sanity check + return true; + +} diff --git a/server/actuators/relays.h b/server/actuators/relays.h new file mode 100644 index 0000000..dcffbf9 --- /dev/null +++ b/server/actuators/relays.h @@ -0,0 +1,18 @@ +#ifndef _RELAY_H +#define _RELAY_H + +#include "../common.h" + +typedef enum +{ + RELAY_CANSELECT, + RELAY_CANENABLE, + RELAY_MAIN +} RelayID; + +extern bool Relay_Init(const char * name, int id); +extern bool Relay_Set(int id, double value); +extern bool Relay_Sanity(int id, double value); +extern bool Relay_Cleanup(int id); + +#endif //_RELAY_H diff --git a/server/bbb_pin.c b/server/bbb_pin.c index 2ffc9e5..bd31365 100644 --- a/server/bbb_pin.c +++ b/server/bbb_pin.c @@ -501,7 +501,8 @@ bool ADC_Read(int id, int *value) if (pread(g_adc[id].fd_value, adc_str, ADC_DIGITS-1, 0) == -1) { - AbortBool("ADC %d read failed: %s", id, strerror(errno)); + //AbortBool("ADC %d read failed: %s", id, strerror(errno)); + return false; } *value = strtol(adc_str, NULL, 10); diff --git a/server/control.c b/server/control.c index 6ebaba7..f81ea00 100644 --- a/server/control.c +++ b/server/control.c @@ -197,7 +197,7 @@ void Control_Handler(FCGIContext *context, char *params) { "%s", name); } - FCGI_AcceptJSON(context, "Ok", NULL); + FCGI_AcceptJSON(context, "Ok"); } } diff --git a/server/control.h b/server/control.h index 7081c3f..0dfdc47 100644 --- a/server/control.h +++ b/server/control.h @@ -18,6 +18,8 @@ typedef enum ControlModes { /** The same as INVALID_CHARACTERS, except escaped for use in JSON strings **/ #define INVALID_CHARACTERS_JSON "\\\"*/:<>?\\\\|. " +#define NOAUTH_USERNAME "_anonymous_noauth" + extern void Control_Handler(FCGIContext *context, char *params); extern const char* Control_SetMode(ControlModes desired_mode, void * arg); extern ControlModes Control_GetMode(); diff --git a/server/data.c b/server/data.c index edfd4c3..19828bc 100644 --- a/server/data.c +++ b/server/data.c @@ -356,3 +356,51 @@ DataFormat Data_GetFormat(FCGIValue * fmt) } return JSON; } + +/** + * Binary search for index of a double in an array + * @param value - The value + * @param x - The array + * @param size - Sizeof the array + */ +int FindClosest(double value, double x[], int size) +{ + int upper = size-1; + int lower = 0; + int index = 0; + while (upper - lower > 1) + { + index = lower + ((upper - lower)/2); + double look = x[index]; + if (look > value) + upper = index; + else if (look < value) + lower = index; + else + return index; + } + + if (x[index] > value && index > 0) + --index; + return index; + +} + +/** + * Get calibrated value by interpolation in array y + * @param value - Raw measured value + * @param x - x values (raw values) of the data + * @param y - calibrated values + * @param size - Number of values in the arrays + * @returns interpolated calibrated value + */ +double Data_Calibrate(double value, double x[], double y[], int size) +{ + int i = FindClosest(value, x, size); + if (i >= size-1) + { + i = size-2; + } + double dist = (value - x[i])/(x[i+1] - x[i]); + return y[i] + dist*(y[i+1]-y[i]); +} diff --git a/server/data.h b/server/data.h index df16efe..d6234fd 100644 --- a/server/data.h +++ b/server/data.h @@ -54,4 +54,6 @@ extern void Data_Handler(DataFile * df, FCGIValue * start, FCGIValue * end, Data extern DataFormat Data_GetFormat(FCGIValue * fmt); // Helper; convert human readable format string to DataFormat +extern double Data_Callibrate(double value, double map[], int map_size); + #endif //_DATAPOINT_H diff --git a/server/dilatometer.c b/server/dilatometer.c index a22fd64..5cfca0a 100644 --- a/server/dilatometer.c +++ b/server/dilatometer.c @@ -11,8 +11,18 @@ // test positions static double test_left, test_right; +// Canny Edge algorithm variables +int edgeThresh = 1; +int lowThreshold; +int const max_lowThreshold = 100; +int ratio = 3; +int kernel_size = 3; + /** Buffer for storing image data. Stored as a **/ -static CvMat * g_data = NULL; +static CvMat * g_srcRGB = NULL; // Source Image +static CvMat * g_srcGray = NULL; // Gray scale of source image +static CvMat * g_edges = NULL; // Detected Edges +static CvMat * g_data = NULL; // Image to mask edges onto /** Camera capture pointer **/ @@ -24,19 +34,7 @@ static CvCapture * g_capture = NULL; void Dilatometer_TestImage() { - CvMat *g_dataRGB; - g_dataRGB = cvCreateMat(480, 640, CV_8UC3); - //Make sure left and right positions are sane - if( test_left < 0) - test_left = 0; - if( test_right > 639) - test_right = 639; - if( test_left > test_right) - { - int tmp = test_right; - test_right = test_left; - test_left = tmp; - } + g_srcRGB = cvCreateMat(480, 640, CV_8UC3); for( int x = 0; x < 640; ++x) { @@ -45,7 +43,7 @@ void Dilatometer_TestImage() CvScalar s; for( int i = 0; i < 3; ++i) { - s.val[i] = 220 + (rand() % 1000) * 1e-2 - (rand() % 1000) * 1e-2; + s.val[i] = 210 + (rand() % 1000) * 1e-0 - (rand() % 1000) * 1e-0; // Produce an exponential decay around left edge if( x < test_left) s.val[i] *= exp( (x - test_left) / 25); @@ -57,7 +55,7 @@ void Dilatometer_TestImage() else s.val[i] *= exp( (test_right - x) / 25); } - cvSet2D(g_dataRGB,y,x,s); + cvSet2D(g_srcRGB,y,x,s); // if( s.val[0] > 200) // printf("row: %d, col: %d, %f\n", y, x, s.val[0]); } @@ -65,9 +63,9 @@ void Dilatometer_TestImage() } if (g_data == NULL) { - g_data = cvCreateMat(g_dataRGB->rows,g_dataRGB->cols,CV_8UC1); //IPL_DEPTH_8U? + g_data = cvCreateMat(g_srcRGB->rows,g_srcRGB->cols,CV_8UC1); //IPL_DEPTH_8U? } - cvCvtColor(g_dataRGB,g_data,CV_RGB2GRAY); + cvCvtColor(g_srcRGB,g_data,CV_RGB2GRAY); } /** @@ -101,11 +99,47 @@ static void Dilatometer_GetImage() //Need to implement camera } +void CannyThreshold() +{ + + if (g_data == NULL) + { + g_data = cvCreateMat(g_srcGray->rows,g_srcGray->cols,CV_8UC1); + } + + if ( g_edges == NULL) + { + g_edges = cvCreateMat(g_srcGray->rows,g_srcGray->cols,CV_8UC1); + } + + //g_data = 0; + cvShowImage("display", g_srcGray); + cvWaitKey(0); + // Reduce noise with a kernel 3x3. Input the grayscale source image, output to edges. (0's mean it's determined from kernel sizes) + cvSmooth( g_srcGray, g_edges, CV_GAUSSIAN, 9, 9 ,0 ,0 ); + + cvShowImage("display", g_edges); + cvWaitKey(0); + + // Find the edges in the image + cvCanny( g_edges, g_edges, lowThreshold, lowThreshold*ratio, kernel_size ); + + cvShowImage("display", g_edges); + cvWaitKey(0); + + // Mask the edges over G_data + //.copyTo( g_data, g_edges); +} + // Test algorithm static void Dilatometer_GetImageTest( ) { - //Test image - Dilatometer_TestImage(); + //Generates Test image + //Dilatometer_TestImage(); + + //Load Test image + g_srcGray = cvLoadImageM ("testimage.jpg",CV_LOAD_IMAGE_GRAYSCALE ); + CannyThreshold(); } @@ -115,14 +149,14 @@ static void Dilatometer_GetImageTest( ) * @returns the average width of the can */ double Dilatometer_Read(int samples) -{ +{ //Get the latest image //Dilatometer_GetImage(); Dilatometer_GetImageTest(); - int width = g_data->cols; - int height = g_data->rows; + int width = g_srcGray->cols; + int height = g_srcGray->rows; // If the number of samples is greater than the image height, sample every row if( samples > height) { @@ -146,13 +180,13 @@ double Dilatometer_Read(int samples) sample_height = ceil(height * (i + 1) / samples) -1; //printf("sample height is %d\n", sample_height); - //CvScalar test = cvGet2D(g_data, 150,300); + //CvScalar test = cvGet2D(g_srcGray, 150,300); //printf("test is %f,%f,%f,%f\n", test.val[0], test.val[1], test.val[2], test.val[3]); for ( int col = 0; col < width; col++) { - CvScalar value = cvGet2D(g_data, sample_height, col); + CvScalar value = cvGet2D(g_srcGray, sample_height, col); if( value.val[0]> THRES) { edges[pos] += (double) col; @@ -191,21 +225,34 @@ int main(int argc, char ** argv) test_right = 500; Dilatometer_Init(); - cvNamedWindow( "display", CV_WINDOW_AUTOSIZE); - cvShowImage("display", g_data); - cvWaitKey(0); +// cvNamedWindow( "display", CV_WINDOW_AUTOSIZE); +// cvShowImage("display", g_data); +// cvWaitKey(0); double width; - for( int i = 0; i < 20; ++i) + /*for( int i = 0; i < 20; ++i) { test_left -= i * (rand() % 1000) * 1e-3; test_right += i * (rand() % 1000) * 1e-3; + + //Make sure left and right positions are sane + if( test_left < 0) + test_left = 0; + if( test_right > 639) + test_right = 639; + if( test_left > test_right) + { + int tmp = test_right; + test_right = test_left; + test_left = tmp; + } + width = Dilatometer_Read(5); cvNamedWindow( "display", CV_WINDOW_AUTOSIZE); - cvShowImage("display", g_data); + cvShowImage("display", g_srcGray); cvWaitKey(0); double expected = test_right - test_left; double perc = 100 * (expected - width) / expected; printf("%d: Left: %.4f. Width: %.4f.\n Right: %.4f. Expected: %.4f. Percentage: %.4f\n", i, test_left, width, test_right, expected, perc); - } + }*/ } diff --git a/server/fastcgi.c b/server/fastcgi.c index d7e057b..c246781 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "common.h" #include "sensor.h" @@ -33,15 +34,11 @@ * @param context The context to work in * @param params User specified paramters: [actuators, sensors] */ -static void IdentifyHandler(FCGIContext *context, char *params) { +static void IdentifyHandler(FCGIContext *context, char *params) +{ bool ident_sensors = false, ident_actuators = false; - char control_key[CONTROL_KEY_BUFSIZ]; - bool has_control; int i; - snprintf(control_key, CONTROL_KEY_BUFSIZ, "%s", getenv("COOKIE_STRING")); - has_control = FCGI_HasControl(context, control_key); - FCGIValue values[2] = {{"sensors", &ident_sensors, FCGI_BOOL_T}, {"actuators", &ident_actuators, FCGI_BOOL_T}}; if (!FCGI_ParseRequest(context, params, values, 2)) @@ -50,11 +47,12 @@ static void IdentifyHandler(FCGIContext *context, char *params) { FCGI_BeginJSON(context, STATUS_OK); FCGI_JSONPair("description", "MCTX3420 Server API (2013)"); FCGI_JSONPair("build_date", __DATE__ " " __TIME__); - struct timespec t; - t.tv_sec = 0; t.tv_nsec = 0; + struct timespec t = {0}; clock_getres(CLOCK_MONOTONIC, &t); FCGI_JSONDouble("clock_getres", TIMEVAL_TO_DOUBLE(t)); FCGI_JSONLong("api_version", API_VERSION); + + bool has_control = FCGI_HasControl(context); FCGI_JSONBool("logged_in", has_control); FCGI_JSONPair("user_name", has_control ? context->user_name : ""); @@ -67,7 +65,9 @@ static void IdentifyHandler(FCGIContext *context, char *params) { if (i > 0) { FCGI_JSONValue(",\n\t\t"); } - FCGI_JSONValue("\"%d\" : \"%s\"", i, Sensor_GetName(i)); + DataPoint d = Sensor_LastData(i); + FCGI_JSONValue("\"%d\" : {\"name\" : \"%s\", \"value\" : [%f,%f] }", + i, Sensor_GetName(i), d.time_stamp, d.value); } FCGI_JSONValue("\n\t}"); } @@ -78,7 +78,9 @@ static void IdentifyHandler(FCGIContext *context, char *params) { if (i > 0) { FCGI_JSONValue(",\n\t\t"); } - FCGI_JSONValue("\"%d\" : \"%s\"", i, Actuator_GetName(i)); + + DataPoint d = Actuator_LastData(i); + FCGI_JSONValue("\"%d\" : {\"name\" : \"%s\", \"value\" : [%f, %f] }", i, Actuator_GetName(i), d.time_stamp, d.value); } FCGI_JSONValue("\n\t}"); } @@ -165,11 +167,12 @@ bool FCGI_LockControl(FCGIContext *context, const char * user_name, UserType use * @param key The control key to be validated. * @return TRUE if authorized, FALSE if not. */ -bool FCGI_HasControl(FCGIContext *context, const char *key) { +bool FCGI_HasControl(FCGIContext *context) +{ time_t now = time(NULL); int result = (now - context->control_timestamp) <= CONTROL_TIMEOUT && - key != NULL && context->control_key[0] != '\0' && - !strcmp(context->control_key, key); + context->control_key[0] != '\0' && + !strcmp(context->control_key, context->received_key); if (result) { context->control_timestamp = now; //Update the control_timestamp } @@ -181,12 +184,50 @@ bool FCGI_HasControl(FCGIContext *context, const char *key) { * Revokes the current control key, if present. * @param context The context to work in */ -void FCGI_ReleaseControl(FCGIContext *context) { +void FCGI_ReleaseControl(FCGIContext *context) +{ *(context->control_key) = 0; // Note: context->user_name should *not* be cleared return; } +/** + * Gets the control cookie + * @param buffer A storage buffer of exactly CONTROL_KEY_BUFSIZ length to + store the control key + */ +void FCGI_GetControlCookie(char buffer[CONTROL_KEY_BUFSIZ]) +{ + const char *cookies = getenv("COOKIE_STRING"); + const char *start = strstr(cookies, "mctxkey="); + + *buffer = 0; //Clear the buffer + if (start != NULL) { + int i; + start += 8; //length of mctxkey= + for (i = 0; i < CONTROL_KEY_BUFSIZ; i++) { + if (*start == 0 || *start == ';') { + break; + } + buffer[i] = *start++; + } + buffer[i] = 0; + } +} + +/** + * Sends the control key to the user as a cookie. + * @param context the context to work in + * @param set Whether to set or unset the control cookie + */ +void FCGI_SendControlCookie(FCGIContext *context, bool set) { + if (set) { + printf("Set-Cookie: mctxkey=%s\r\n", context->control_key); + } else { + printf("Set-Cookie: mctxkey=\r\n"); + } +} + /** * Extracts a key/value pair from a request string. * Note that the input is modified by this function. @@ -347,12 +388,9 @@ void FCGI_BeginJSON(FCGIContext *context, StatusCodes status_code) * @param description A short description. * @param cookie Optional. If given, the cookie field is set to that value. */ -void FCGI_AcceptJSON(FCGIContext *context, const char *description, const char *cookie) +void FCGI_AcceptJSON(FCGIContext *context, const char *description) { printf("Content-type: application/json; charset=utf-8\r\n"); - if (cookie) { - printf("Set-Cookie: %s\r\n", cookie); - } printf("\r\n{\r\n"); printf("\t\"module\" : \"%s\"", context->current_module); FCGI_JSONLong("status", STATUS_OK); @@ -500,6 +538,42 @@ char *FCGI_EscapeText(char *buf) return buf; } +/** + * Unescapes a URL encoded string in-place. The string + * must be NULL terminated. + * (e.g this%2d+string --> this- string) + * @param buf The buffer to decode. Will be modified in-place. + * @return The same buffer. + */ +char *FCGI_URLDecode(char *buf) +{ + char *head = buf, *tail = buf; + char val, hex[3] = {0}; + + while (*tail) { + if (*tail == '%') { //%hh hex to char + tail++; + if (isxdigit(*tail) && isxdigit(*(tail+1))) { + hex[0] = *tail++; + hex[1] = *tail++; + char val = (char)strtol(hex, NULL, 16); + //Control codes --> Space character + *head++ = (val < 0x20) ? 0x20 : val; + } else { //Not valid format; keep original + head++; + } + } else if (*tail == '+') { //Plus to space + tail++; + *head++ = ' '; + } else { //Anything else + *head++ = *tail++; + } + } + *head = 0; //NULL-terminate at new end point + + return buf; +} + /** * Main FCGI request loop that receives/responds to client requests. * @param data Reserved. @@ -514,32 +588,19 @@ void * FCGI_RequestLoop (void *data) while (FCGI_Accept() >= 0) { ModuleHandler module_handler = NULL; - char module[BUFSIZ], params[BUFSIZ], control_key[CONTROL_KEY_BUFSIZ]; + char module[BUFSIZ], params[BUFSIZ]; //strncpy doesn't zero-truncate properly snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL")); - snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING")); - - //char cookies[BUFSIZ]; - //snprintf(cookies, BUFSIZ, "%s", getenv("COOKIE_STRING")); - //Log(LOGDEBUG, "ALL cookies %s", cookies); //mmmm - - //Hack to get the nameless cookie only - // (works as long as browsers send the nameless cookie first...) - snprintf(control_key, CONTROL_KEY_BUFSIZ, "%s", getenv("COOKIE_STRING")); - // Ignore any other cookies if the nameless cookie is empty - for (int i = 0; i < CONTROL_KEY_BUFSIZ; ++i) - { - if (control_key[i] == ';') - { - control_key[i] = '\0'; - break; - } - } + //Get the GET query string + snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING")); + //URL decode the parameters + FCGI_URLDecode(params); + FCGI_GetControlCookie(context.received_key); Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params); - Log(LOGDEBUG, "Control key: %s", control_key); + Log(LOGDEBUG, "Control key: %s", context.received_key); //Remove trailing slashes (if present) from module query @@ -572,26 +633,38 @@ void * FCGI_RequestLoop (void *data) context.current_module = module; context.response_number++; - if (module_handler) - { - if (g_options.auth_method != AUTH_NONE && module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler) - //if (false) // Testing - { - if (!FCGI_HasControl(&context, control_key)) + if (module_handler) { + if (module_handler == IdentifyHandler) { + FCGI_EscapeText(params); + } else if (module_handler != Login_Handler) { + if (!FCGI_HasControl(&context)) { - FCGI_RejectJSON(&context, "Please login. Invalid control key."); - continue; + if (g_options.auth_method == AUTH_NONE) { //:( + Log(LOGWARN, "Locking control (no auth!)"); + FCGI_LockControl(&context, NOAUTH_USERNAME, USER_ADMIN); + FCGI_SendControlCookie(&context, true); + } + else { + FCGI_RejectJSON(&context, "Please login. Invalid control key."); + continue; + } } - + //Escape all special characters. //Don't escape for login (password may have special chars?) FCGI_EscapeText(params); + } else { //Only for Login handler. + //If GET data is empty, use POST instead. + if (*params == '\0') { + Log(LOGDEBUG, "Using POST!"); + fgets(params, BUFSIZ, stdin); + FCGI_URLDecode(params); + } } module_handler(&context, params); } - else - { + else { FCGI_RejectJSON(&context, "Unhandled module"); } } diff --git a/server/fastcgi.h b/server/fastcgi.h index de02501..a417a43 100644 --- a/server/fastcgi.h +++ b/server/fastcgi.h @@ -50,6 +50,8 @@ typedef struct time_t control_timestamp; /**A SHA-1 hash that is the control key, determining who is logged in**/ char control_key[CONTROL_KEY_BUFSIZ]; + /**The received control key for the current request**/ + char received_key[CONTROL_KEY_BUFSIZ]; /**The IPv4 address of the logged-in user**/ char control_ip[16]; /**Determines if the user is an admin or not**/ @@ -68,11 +70,13 @@ typedef void (*ModuleHandler) (FCGIContext *context, char *params); extern bool FCGI_LockControl(FCGIContext *context, const char * user_name, UserType user_type); extern void FCGI_ReleaseControl(FCGIContext *context); -extern bool FCGI_HasControl(FCGIContext *context, const char *key); +extern bool FCGI_HasControl(FCGIContext *context); +extern void FCGI_GetControlCookie(char buffer[CONTROL_KEY_BUFSIZ]); +extern void FCGI_SendControlCookie(FCGIContext *context, bool set); extern char *FCGI_KeyPair(char *in, const char **key, const char **value); extern bool FCGI_ParseRequest(FCGIContext *context, char *params, FCGIValue values[], size_t count); extern void FCGI_BeginJSON(FCGIContext *context, StatusCodes status_code); -extern void FCGI_AcceptJSON(FCGIContext *context, const char *description, const char *cookie); +extern void FCGI_AcceptJSON(FCGIContext *context, const char *description); extern void FCGI_JSONPair(const char *key, const char *value); extern void FCGI_JSONLong(const char *key, long value); extern void FCGI_JSONDouble(const char *key, double value); @@ -81,6 +85,7 @@ extern void FCGI_JSONKey(const char *key); extern void FCGI_PrintRaw(const char *format, ...); extern void FCGI_EndJSON(); extern void FCGI_RejectJSONEx(FCGIContext *context, StatusCodes status, const char *description); +extern char *FCGI_URLDecode(char *buf); extern char *FCGI_EscapeText(char *buf); extern void *FCGI_RequestLoop (void *data); diff --git a/server/filetest b/server/filetest new file mode 100644 index 0000000..945da8f --- /dev/null +++ b/server/filetest @@ -0,0 +1 @@ +0.000000 diff --git a/server/login.c b/server/login.c index 30105cb..3445c04 100644 --- a/server/login.c +++ b/server/login.c @@ -278,14 +278,18 @@ int Login_LDAP_Bind(const char * uri, const char * dn, const char * pass) void Logout_Handler(FCGIContext * context, char * params) { FCGI_ReleaseControl(context); - FCGI_AcceptJSON(context, "Logged out", "0"); + FCGI_SendControlCookie(context, false); //Unset the cookie + FCGI_AcceptJSON(context, "Logged out"); } /** * Handle a Login Request * @param context - The context - * @param params - Parameter string, should contain username and password + * @param params - Parameter string, should contain username and password. + * NOTE: Care should be taken when using params, as it is + * completely unescaped. Do not log or use it without + * suitable escaping. */ void Login_Handler(FCGIContext * context, char * params) { @@ -327,7 +331,7 @@ void Login_Handler(FCGIContext * context, char * params) case AUTH_LDAP: { - if (strlen(pass) <= 0) + if (*pass == '\0') { FCGI_RejectJSON(context, "No password supplied."); return; @@ -419,7 +423,8 @@ void Login_Handler(FCGIContext * context, char * params) { FCGI_EscapeText(context->user_name); //Don't break javascript pls // Give the user a cookie - FCGI_AcceptJSON(context, "Logged in", context->control_key); + FCGI_SendControlCookie(context, true); //Send the control key + FCGI_AcceptJSON(context, "Logged in"); Log(LOGDEBUG, "Successful authentication for %s", user); } else diff --git a/server/main.c b/server/main.c index 38fc9f0..2ca053e 100644 --- a/server/main.c +++ b/server/main.c @@ -160,7 +160,7 @@ void Cleanup() { Log(LOGDEBUG, "Begin cleanup."); Sensor_Cleanup(); - //Actuator_Cleanup(); + Actuator_Cleanup(); Log(LOGDEBUG, "Finish cleanup."); } @@ -236,23 +236,22 @@ int main(int argc, char ** argv) - Sensor_Init(); - Actuator_Init(); Pin_Init(); // Try and start things - const char *ret; - if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL) - Fatal("Control_SetMode failed with '%s'", ret); + //const char *ret; + //if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL) + // Fatal("Control_SetMode failed with '%s'", ret); // run request thread in the main thread FCGI_RequestLoop(NULL); - if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL) - Fatal("Control_SetMode failed with '%s'", ret); + Control_SetMode(CONTROL_STOP, NULL); + //if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL) + // Fatal("Control_SetMode failed with '%s'", ret); //Sensor_StopAll(); //Actuator_StopAll(); diff --git a/server/microscope.c b/server/microscope.c new file mode 100644 index 0000000..e388073 --- /dev/null +++ b/server/microscope.c @@ -0,0 +1,246 @@ +/** + * @file microscope.c + * @purpose Implementation of microscope related functions + */ + +#include "cv.h" +#include "highgui_c.h" +#include "microscope.h" +#include + +// test positions +static double test_left, test_right; + +// Canny Edge algorithm variables +int lowThreshold = 30; +int ratio = 3; +int kernel_size = 3; + +/** Buffer for storing image data. Stored as a **/ +static CvMat * g_srcRGB = NULL; // Source Image +static CvMat * g_srcGray = NULL; // Gray scale of source image +static CvMat * g_edges = NULL; // Detected Edges +static CvMat * g_data = NULL; // Image to mask edges onto + + +/** Camera capture pointer **/ +static CvCapture * g_capture = NULL; + +/** + * Create a test image using left as left edge and right as right edge positions + */ +void Dilatometer_TestImage() +{ + + g_srcRGB = cvCreateMat(480, 640, CV_8UC3); + + for( int x = 0; x < 640; ++x) + { + for (int y = 0; y < 480; ++y) + { + CvScalar s; + for( int i = 0; i < 3; ++i) + { + s.val[i] = 210 + (rand() % 1000) * 1e-0 - (rand() % 1000) * 1e-0; + // Produce an exponential decay around left edge + if( x < test_left) + s.val[i] *= exp( (x - test_left) / 25); + else if( x < 320) + s.val[i] *= exp( (test_left - x) / 25); + // Produce an exponential decay around right edge + else if( x < test_right) + s.val[i] *= exp( (x - test_right) / 25); + else + s.val[i] *= exp( (test_right - x) / 25); + } + cvSet2D(g_srcRGB,y,x,s); + // if( s.val[0] > 200) + // printf("row: %d, col: %d, %f\n", y, x, s.val[0]); + } + + } + if (g_data == NULL) + { + g_data = cvCreateMat(g_srcRGB->rows,g_srcRGB->cols,CV_8UC1); //IPL_DEPTH_8U? + } + cvCvtColor(g_srcRGB,g_data,CV_RGB2GRAY); +} + +/** + * Initialise the dilatometer + */ +void Microscope_Init() +{ + + // Make an initial reading (will allocate memory the first time only). + double val; + Microscope_Read(&val, 1); +} + +/** + * Cleanup Interferometer stuff + */ +void Microscope_Cleanup() +{ + if (g_data != NULL) + cvReleaseMat(&g_data); + + if (g_capture != NULL) + cvReleaseCapture(&g_capture); + +} + +/** + * Get an image from the Dilatometer + */ +static void Microscope_GetImage() +{ + //Need to implement camera +} + +void CannyThreshold() +{ + + if (g_data == NULL) + { + g_data = cvCreateMat(g_srcGray->rows,g_srcGray->cols,CV_8UC1); + } + + if ( g_edges == NULL) + { + g_edges = cvCreateMat(g_srcGray->rows,g_srcGray->cols,CV_8UC1); + } + + //g_data = 0; + cvShowImage("display", g_srcGray); + cvWaitKey(0); + // Reduce noise with a kernel 3x3. Input the grayscale source image, output to edges. (0's mean it's determined from kernel sizes) + cvSmooth( g_srcGray, g_edges, CV_GAUSSIAN, 9, 9 ,0 ,0 ); + + cvShowImage("display", g_edges); + cvWaitKey(0); + + // Find the edges in the image + lowThreshold = 35; + cvCanny( g_edges, g_edges, lowThreshold, lowThreshold*ratio, kernel_size ); + + cvShowImage("display", g_edges); + cvWaitKey(0); + + // Mask the edges over G_data + //.copyTo( g_data, g_edges); +} + +// Test algorithm +static void Microscope_GetImageTest( ) +{ + //Generates Test image + //Dilatometer_TestImage(); + + //Load Test image + g_srcGray = cvLoadImageM ("testimage.jpg",CV_LOAD_IMAGE_GRAYSCALE ); + CannyThreshold(); +} + + + /** + * Read the microscope image. The value changed will correspond to the new location of the edge. + * @param val - Will store the read value if successful + * @param samples - Number of rows to scan (increasing will slow down performance!) + * @returns true on successful read + */ +bool Microscope_Read( double * value, int samples) +{ + bool result = false; + double average = 0; + // Get the image from the camera + Microscope_GetImageTest(); + + int width = g_edges->cols; + int height = g_edges->rows; + + // If the number of samples is greater than the image height, sample every row + if( samples > height) + { + samples = height; + } + + int sample_height; + int num_edges = 0; // Number of edges. if each sample location has an edge, then num_edges = samples + + for (int i=0; i THRES) + { + edge_location += col; + num++; + } + } + if( num > 0) + { + average += ( edge_location / num ); + num_edges++; + printf("average %f\n", average/num_edges); + } + } + if (num_edges > 0) + average /= num_edges; + + if( average > 0) + { + result = true; //Successfully found an edge + *value = average; + } + return result; +} + +// Overlays a line over the given edge position +void Draw_Edge(double edge) +{ + CvScalar value; + value.val[0]=244; + for( int i = 0; i < g_srcGray->rows; i++) + { + cvSet2D(g_edges,i,edge,value); + } + cvShowImage("display", g_edges); + cvWaitKey(0); +} + +/** + * For testing purposes + */ +int main(int argc, char ** argv) +{ + //cvNamedWindow( "display", CV_WINDOW_AUTOSIZE );// Create a window for display. + //gettimeofday(&start, NULL); + test_left = 100; + test_right = 500; + Microscope_Init(); + + cvNamedWindow( "display", CV_WINDOW_AUTOSIZE); +// cvShowImage("display", g_data); +// cvWaitKey(0); + double width; + + double edge; + Microscope_Read(&edge,15); + //For testing purposes, overlay the given average line over the image + Draw_Edge(edge); + +} + diff --git a/server/microscope.h b/server/microscope.h new file mode 100644 index 0000000..ca63b5e --- /dev/null +++ b/server/microscope.h @@ -0,0 +1,14 @@ +/** + * @file microscope.h + * @brief Declarations for functions to deal with microscope + */ + +#include "common.h" + +//Threshold to determine the edge of the can +#define THRES 230 + +extern void Microscope_Init(); // Initialise the dilatometer +extern void Microscope_Cleanup(); // Cleanup +extern bool Microscope_Read( double * value, int samples); // Read the Microscope + diff --git a/server/parameters b/server/parameters index 5fdc595..26e2892 100644 --- a/server/parameters +++ b/server/parameters @@ -17,16 +17,19 @@ LOGDEBUG=4 verbosity="$LOGDEBUG" # Set to 1/0 to enable/disable the pin module (gives direct control over GPIO/ADC/PWM) -#TODO: This option isn't actually implemented yet... pin_test="0" # Set to the URI to use authentication -#auth_uri="ldap://192.168.1.1#ou=People,dc=daedalus" -#auth_uri="ldaps://ldap.pheme.uwa.edu.au#ou=Users,ou=UWA,dc=uwads,dc=uwa,dc=edu,dc=au" #UWA +#auth_uri="ldap://192.168.1.1" +#auth_uri="ldaps://ldap.pheme.uwa.edu.au" #UWA #auth_uri="/etc/shadow" #auth_uri="shadow" -auth_uri="mysql://localhost#root,$(cat mysql_password)" + +# Set to the dn of the LDAP server +ldap_base_dn="ou=People,dc=daedalus" # Testing +#ldap_base_dn="ou=Users,ou=UWA,dc=uwads,dc=uwa,dc=edu,dc=au" #UWA ## OPTIONS TO BE PASSED TO SERVER; DO NOT EDIT -parameters="-v $verbosity -p $pin_test -A $auth_uri" +parameters="-v $verbosity -p $pin_test" +# -A $auth_uri -d $ldap_base_dn" diff --git a/server/sensor.c b/server/sensor.c index ae5cdf4..53b3f03 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -47,7 +47,7 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn s->init = init; // Set init function // Start by averaging values taken over a second - DOUBLE_TO_TIMEVAL(1e-4, &(s->sample_time)); + DOUBLE_TO_TIMEVAL(1, &(s->sample_time)); s->averages = 1; s->num_read = 0; @@ -62,6 +62,8 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn s->current_data.time_stamp = 0; s->current_data.value = 0; + s->averaged_data.time_stamp = 0; + s->averaged_data.value = 0; return g_num_sensors; } @@ -75,16 +77,16 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn #include "sensors/pressure.h" void Sensor_Init() { - Sensor_Add("cpu_stime", RESOURCE_CPU_SYS, Resource_Read, NULL, NULL, NULL); - Sensor_Add("cpu_utime", RESOURCE_CPU_USER, Resource_Read, NULL, NULL, NULL); - //Sensor_Add("pressure_high0", PRES_HIGH0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); - //Sensor_Add("pressure_high1", PRES_HIGH1, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); - //Sensor_Add("pressure_low0", PRES_LOW0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + //Sensor_Add("cpu_stime", RESOURCE_CPU_SYS, Resource_Read, NULL, NULL, NULL); + //Sensor_Add("cpu_utime", RESOURCE_CPU_USER, Resource_Read, NULL, NULL, NULL); + Sensor_Add("pressure_high0", PRES_HIGH0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + Sensor_Add("pressure_high1", PRES_HIGH1, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); + Sensor_Add("pressure_low0", PRES_LOW0, Pressure_Read, Pressure_Init, Pressure_Cleanup, NULL); //Sensor_Add("../testing/count.py", 0, Piped_Read, Piped_Init, Piped_Cleanup, 1e50,-1e50,1e50,-1e50); - //Sensor_Add("strain0", STRAIN0, Strain_Read, Strain_Init, 5000,0,5000,0); - //Sensor_Add("strain1", STRAIN1, Strain_Read, Strain_Init, 5000,0,5000,0); - //Sensor_Add("strain2", STRAIN2, Strain_Read, Strain_Init, 5000,0,5000,0); - //Sensor_Add("strain3", STRAIN3, Strain_Read, Strain_Init, 5000,0,5000,0); + Sensor_Add("strain0", STRAIN0, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); + Sensor_Add("strain1", STRAIN1, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); + Sensor_Add("strain2", STRAIN2, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); + Sensor_Add("strain3", STRAIN3, Strain_Read, Strain_Init, Strain_Cleanup, Strain_Sanity); //Sensor_Add("pressure0", PRESSURE0, Pressure_Read, Pressure_Init, 5000,0,5000,0); //Sensor_Add("pressure1", PRESSURE1, Pressure_Read, Pressure_Init, 5000,0,5000,0); //Sensor_Add("pressure_feedback", PRESSURE_FEEDBACK, Pressure_Read, Pressure_Init, 5000,0,5000,0); @@ -103,6 +105,7 @@ void Sensor_Cleanup() if (s->cleanup != NULL) s->cleanup(s->user_id); } + g_num_sensors = 0; } /** @@ -183,8 +186,12 @@ void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg) */ void Sensor_SetModeAll(ControlModes mode, void * arg) { + if (mode == CONTROL_START) + Sensor_Init(); for (int i = 0; i < g_num_sensors; i++) Sensor_SetMode(&g_sensors[i], mode, arg); + if (mode == CONTROL_STOP) + Sensor_Cleanup(); } @@ -201,38 +208,40 @@ void * Sensor_Loop(void * arg) // Until the sensor is stopped, record data points while (s->activated) { - DataPoint d; - d.value = 0; - bool success = s->read(s->user_id, &(d.value)); + + bool success = s->read(s->user_id, &(s->current_data.value)); struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - d.time_stamp = TIMEVAL_DIFF(t, *Control_GetStartTime()); + s->current_data.time_stamp = TIMEVAL_DIFF(t, *Control_GetStartTime()); if (success) { if (s->sanity != NULL) { - if (!s->sanity(s->user_id, d.value)) + if (!s->sanity(s->user_id, s->current_data.value)) { Fatal("Sensor %s (%d,%d) reads unsafe value", s->name, s->id, s->user_id); } } - s->current_data.time_stamp += d.time_stamp; - s->current_data.value += d.value; + s->averaged_data.time_stamp += s->current_data.time_stamp; + s->averaged_data.value = s->current_data.value; if (++(s->num_read) >= s->averages) { - s->current_data.time_stamp /= s->averages; - s->current_data.value /= s->averages; - Data_Save(&(s->data_file), &(s->current_data), 1); // Record it + s->averaged_data.time_stamp /= s->averages; + s->averaged_data.value /= s->averages; + Data_Save(&(s->data_file), &(s->averaged_data), 1); // Record it s->num_read = 0; - s->current_data.time_stamp = 0; - s->current_data.value = 0; + s->averaged_data.time_stamp = 0; + s->averaged_data.value = 0; } } else - Log(LOGWARN, "Failed to read sensor %s (%d,%d)", s->name, s->id,s->user_id); + { + // Silence because strain sensors fail ~50% of the time :S + //Log(LOGWARN, "Failed to read sensor %s (%d,%d)", s->name, s->id,s->user_id); + } clock_nanosleep(CLOCK_MONOTONIC, 0, &(s->sample_time), NULL); @@ -408,5 +417,10 @@ const char * Sensor_GetName(int id) return g_sensors[id].name; } +DataPoint Sensor_LastData(int id) +{ + Sensor * s = &(g_sensors[id]); + return s->current_data; +} diff --git a/server/sensor.h b/server/sensor.h index 0188361..3de48e0 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -61,6 +61,9 @@ typedef struct int averages; /** Current data **/ DataPoint current_data; + + /** Summed data **/ + DataPoint averaged_data; /** Number of points read so far before applying average **/ int num_read; @@ -82,6 +85,8 @@ extern Sensor * Sensor_Identify(const char * str); // Identify a Sensor from a s extern void Sensor_Handler(FCGIContext *context, char * params); // Handle a FCGI request for Sensor data +extern DataPoint Sensor_LastData(int id); + extern const char * Sensor_GetName(int id); diff --git a/server/sensors/common.c b/server/sensors/common.c new file mode 100644 index 0000000..cc17f0d --- /dev/null +++ b/server/sensors/common.c @@ -0,0 +1,3 @@ +#include "common.h" + + diff --git a/server/sensors/microphone.c b/server/sensors/microphone.c new file mode 100644 index 0000000..63a9d71 --- /dev/null +++ b/server/sensors/microphone.c @@ -0,0 +1,40 @@ +/** + * @file microphone.c + * @purpose Implementation of Pressure reading functions + */ + +#include "microphone.h" +#include "../bbb_pin.h" +#include "../log.h" // For Fatal() + +#define MIC_ADC ADC2 + +double adc_raw[] = {524,668,733,991,1121,1264,1300,1437,1645,1789,1932,2033,2105,2148,2284,2528,3089}; +double mic_cal[] = {70,73,75,76.8,77.7,80,81.2,83.3,85.5,87.5,90.7,92.6,94.3,96.2,100,102,125}; + +bool Microphone_Init(const char * name, int id) +{ + assert(sizeof(adc_raw) == sizeof(mic_cal)); + return ADC_Export(MIC_ADC); +} + +bool Microphone_Cleanup(int id) +{ + ADC_Unexport(MIC_ADC); + return true; +} + +bool Microphone_Read(int id, double * value) +{ + int adc = 0; + if (!ADC_Read(MIC_ADC, &adc)) + return false; + + *value = Data_Calibrate((double)adc, adc_raw, mic_cal, sizeof(adc_raw)/sizeof(double)); + return true; +} + +bool Microphone_Sanity(int id, double value) +{ + return true; +} diff --git a/server/sensors/microphone.h b/server/sensors/microphone.h new file mode 100644 index 0000000..371506b --- /dev/null +++ b/server/sensors/microphone.h @@ -0,0 +1,11 @@ +#ifndef _MICROPHONE_H +#define _MICROPHONE_H + +extern bool Microphone_Init(const char * name, int id); +extern bool Microphone_Cleanup(int id); +extern bool Microphone_Read(int id, double * value); +extern bool Microphone_Sanity(int id, double value); + +#endif //_MICROPHONE_H + + diff --git a/server/sensors/pressure.c b/server/sensors/pressure.c index 61cdbcf..699dda3 100644 --- a/server/sensors/pressure.c +++ b/server/sensors/pressure.c @@ -6,9 +6,15 @@ #include "pressure.h" #include "../bbb_pin.h" #include "../log.h" // For Fatal() +#include "../data.h" #define PSI_TO_KPA 6.89475729 +/** Uncalibrated values in ADC readings **/ +static double high_raw[] = {642,910,1179,1445,1712,1980}; +/** Calibrated values in kPa **/ +static double high_cal[] = {95, 190, 285, 380, 474, 560}; + /** * Get the ADC number of a Pressure sensor * @param id - Id of the sensor @@ -40,18 +46,26 @@ double Pressure_Callibrate(int id, int adc) { //double voltage = ADC_TO_VOLTS(adc); // convert reading to voltage + + + //TODO: Fix this switch (id) { case PRES_HIGH0: case PRES_HIGH1: { + /* static const double Vs = 5e3; // In mVs static const double Pmin = 0.0 * PSI_TO_KPA; static const double Pmax = 150.0 * PSI_TO_KPA; double Vout = ADC_TO_MVOLTS(adc); return ((Vout - 0.1*Vs)/(0.8*Vs))*(Pmax - Pmin) + Pmin; + */ + + return Data_Calibrate((double)adc, high_raw, high_cal, sizeof(high_raw)/sizeof(double)); } case PRES_LOW0: + // Not calibrated! return (200.0 * (adc / ADC_RAW_MAX)); default: Fatal("Unknown Pressure id %d", id); @@ -102,3 +116,13 @@ bool Pressure_Read(int id, double * value) //pthread_mutex_unlock(&mutex); return result; } + +/** + * Sanity check the pressure reading + * @param value - The pressure reading (calibrated) + * @returns true iff the value is safe, false if it is dangerous + */ +bool Pressure_Sanity(int id, double value) +{ + return (value < 590); +} diff --git a/server/sensors/pressure.h b/server/sensors/pressure.h index 22d1adc..a37d918 100644 --- a/server/sensors/pressure.h +++ b/server/sensors/pressure.h @@ -17,6 +17,7 @@ typedef enum extern bool Pressure_Init(const char * name, int id); extern bool Pressure_Cleanup(int id); extern bool Pressure_Read(int id, double * value); +extern bool Pressure_Sanity(int id, double value); #endif //_PRESSURE_H diff --git a/server/sensors/strain.c b/server/sensors/strain.c index 9492de7..7c78b56 100644 --- a/server/sensors/strain.c +++ b/server/sensors/strain.c @@ -6,6 +6,8 @@ #include #define STRAIN_ADC ADC0 +// TODO: Choose this +#define STRAIN_GPIO 45 /** * Convert Strain gauge id number to a GPIO pin on the Mux @@ -23,13 +25,13 @@ static int Strain_To_GPIO(StrainID id) switch (id) { case STRAIN0: - return GPIO0_30; + return 44; case STRAIN1: - return GPIO1_28; + return 26; case STRAIN2: - return GPIO0_31; + return 46; case STRAIN3: - return GPIO1_16; + return 65; default: Fatal("Unknown StrainID %d", id); return -1; // Should never happen @@ -59,15 +61,36 @@ bool Strain_Init(const char * name, int id) if (!GPIO_Set(gpio_num, false)) Fatal("Couldn't set GPIO%d for strain sensor %d to LOW", gpio_num, id); - static bool init_adc = false; - if (!init_adc) + static int init = 0; + if (++init == 1) { - init_adc = true; + GPIO_Export(STRAIN_GPIO); + GPIO_Set(STRAIN_GPIO, true); ADC_Export(STRAIN_ADC); } return true; } +bool Strain_Cleanup(int id) +{ + static int killed = 0; + if (++killed == 4) + { + + GPIO_Set(STRAIN_GPIO, false); + ADC_Unexport(STRAIN_ADC); + } + + int gpio_num = Strain_To_GPIO(id); + GPIO_Unexport(gpio_num); + return true; +} + +bool Strain_Sanity(int id, double value) +{ + return true; +} + /** * Read from a Strain gauge * @param id - The strain gauge to read diff --git a/server/sensors/strain.h b/server/sensors/strain.h index 1afb625..21b926e 100644 --- a/server/sensors/strain.h +++ b/server/sensors/strain.h @@ -19,4 +19,7 @@ extern bool Strain_Init(const char * name, int id); // Read from a strain gauge extern bool Strain_Read(int id, double * value); +extern bool Strain_Cleanup(int id); +extern bool Strain_Sanity(int id, double value); + #endif //_STRAIN_H diff --git a/testing/Camera/test 1/test_blurred.jpg b/testing/Camera/test 1/test_blurred.jpg new file mode 100644 index 0000000..78c484d Binary files /dev/null and b/testing/Camera/test 1/test_blurred.jpg differ diff --git a/testing/Camera/test 1/test_edge.jpg b/testing/Camera/test 1/test_edge.jpg new file mode 100644 index 0000000..9b076f8 Binary files /dev/null and b/testing/Camera/test 1/test_edge.jpg differ diff --git a/testing/Camera/test 1/test_edge_avg.jpg b/testing/Camera/test 1/test_edge_avg.jpg new file mode 100644 index 0000000..45ff0bd Binary files /dev/null and b/testing/Camera/test 1/test_edge_avg.jpg differ diff --git a/testing/Camera/test 1/testimage.jpg b/testing/Camera/test 1/testimage.jpg new file mode 100644 index 0000000..caaf9e6 Binary files /dev/null and b/testing/Camera/test 1/testimage.jpg differ diff --git a/testing/Camera/test 1/values b/testing/Camera/test 1/values new file mode 100644 index 0000000..f7e99b7 --- /dev/null +++ b/testing/Camera/test 1/values @@ -0,0 +1,2 @@ +Blur = 9,9 +Low threshold = 35 diff --git a/testing/Camera/test 1/values~ b/testing/Camera/test 1/values~ new file mode 100644 index 0000000..e69de29 diff --git a/testing/Camera/test 2/test_blurred.jpg b/testing/Camera/test 2/test_blurred.jpg new file mode 100644 index 0000000..72ece1a Binary files /dev/null and b/testing/Camera/test 2/test_blurred.jpg differ diff --git a/testing/Camera/test 2/test_edge.jpg b/testing/Camera/test 2/test_edge.jpg new file mode 100644 index 0000000..4e87ccb Binary files /dev/null and b/testing/Camera/test 2/test_edge.jpg differ diff --git a/testing/Camera/test 2/test_edge_avg.jpg b/testing/Camera/test 2/test_edge_avg.jpg new file mode 100644 index 0000000..bc6248b Binary files /dev/null and b/testing/Camera/test 2/test_edge_avg.jpg differ diff --git a/testing/Camera/test 2/testimage2.jpg b/testing/Camera/test 2/testimage2.jpg new file mode 100644 index 0000000..ef47265 Binary files /dev/null and b/testing/Camera/test 2/testimage2.jpg differ diff --git a/testing/Camera/test 2/values b/testing/Camera/test 2/values new file mode 100644 index 0000000..f7e99b7 --- /dev/null +++ b/testing/Camera/test 2/values @@ -0,0 +1,2 @@ +Blur = 9,9 +Low threshold = 35 diff --git a/testing/Camera/test 3/test_blurred.jpg b/testing/Camera/test 3/test_blurred.jpg new file mode 100644 index 0000000..e76fa63 Binary files /dev/null and b/testing/Camera/test 3/test_blurred.jpg differ diff --git a/testing/Camera/test 3/test_edge.jpg b/testing/Camera/test 3/test_edge.jpg new file mode 100644 index 0000000..e35e374 Binary files /dev/null and b/testing/Camera/test 3/test_edge.jpg differ diff --git a/testing/Camera/test 3/test_edge_avg.jpg b/testing/Camera/test 3/test_edge_avg.jpg new file mode 100644 index 0000000..7d41059 Binary files /dev/null and b/testing/Camera/test 3/test_edge_avg.jpg differ diff --git a/testing/Camera/test 3/testimage2.jpg b/testing/Camera/test 3/testimage2.jpg new file mode 100644 index 0000000..ef47265 Binary files /dev/null and b/testing/Camera/test 3/testimage2.jpg differ diff --git a/testing/Camera/test 3/values b/testing/Camera/test 3/values new file mode 100644 index 0000000..0ed6414 --- /dev/null +++ b/testing/Camera/test 3/values @@ -0,0 +1,2 @@ +Blur = 5,5 +Low threshold = 40 diff --git a/testing/Camera/test 3/values~ b/testing/Camera/test 3/values~ new file mode 100644 index 0000000..f7e99b7 --- /dev/null +++ b/testing/Camera/test 3/values~ @@ -0,0 +1,2 @@ +Blur = 9,9 +Low threshold = 35 diff --git a/testing/Camera/test 4/test_blurred.jpg b/testing/Camera/test 4/test_blurred.jpg new file mode 100644 index 0000000..73c4e62 Binary files /dev/null and b/testing/Camera/test 4/test_blurred.jpg differ diff --git a/testing/Camera/test 4/test_edge.jpg b/testing/Camera/test 4/test_edge.jpg new file mode 100644 index 0000000..323b365 Binary files /dev/null and b/testing/Camera/test 4/test_edge.jpg differ diff --git a/testing/Camera/test 4/test_edge_avg.jpg b/testing/Camera/test 4/test_edge_avg.jpg new file mode 100644 index 0000000..7849485 Binary files /dev/null and b/testing/Camera/test 4/test_edge_avg.jpg differ diff --git a/testing/Camera/test 4/testimage2.jpg b/testing/Camera/test 4/testimage2.jpg new file mode 100644 index 0000000..ef47265 Binary files /dev/null and b/testing/Camera/test 4/testimage2.jpg differ diff --git a/testing/Camera/test 4/values b/testing/Camera/test 4/values new file mode 100644 index 0000000..2ad1fb1 --- /dev/null +++ b/testing/Camera/test 4/values @@ -0,0 +1,2 @@ +Blur = 7,7 +Low threshold = 36 diff --git a/testing/Camera/test 4/values~ b/testing/Camera/test 4/values~ new file mode 100644 index 0000000..2ad1fb1 --- /dev/null +++ b/testing/Camera/test 4/values~ @@ -0,0 +1,2 @@ +Blur = 7,7 +Low threshold = 36 diff --git a/testing/Camera/test 5/test_blurred.jpg b/testing/Camera/test 5/test_blurred.jpg new file mode 100644 index 0000000..e136542 Binary files /dev/null and b/testing/Camera/test 5/test_blurred.jpg differ diff --git a/testing/Camera/test 5/test_edge.jpg b/testing/Camera/test 5/test_edge.jpg new file mode 100644 index 0000000..2100541 Binary files /dev/null and b/testing/Camera/test 5/test_edge.jpg differ diff --git a/testing/Camera/test 5/test_edge_avg.jpg b/testing/Camera/test 5/test_edge_avg.jpg new file mode 100644 index 0000000..9cbc9a4 Binary files /dev/null and b/testing/Camera/test 5/test_edge_avg.jpg differ diff --git a/testing/Camera/test 5/testimage.jpg b/testing/Camera/test 5/testimage.jpg new file mode 100644 index 0000000..caaf9e6 Binary files /dev/null and b/testing/Camera/test 5/testimage.jpg differ diff --git a/testing/Camera/test 5/values b/testing/Camera/test 5/values new file mode 100644 index 0000000..0149619 --- /dev/null +++ b/testing/Camera/test 5/values @@ -0,0 +1,2 @@ +Blur = 5,5 +Low threshold = 35 diff --git a/testing/Camera/test 5/values~ b/testing/Camera/test 5/values~ new file mode 100644 index 0000000..2ad1fb1 --- /dev/null +++ b/testing/Camera/test 5/values~ @@ -0,0 +1,2 @@ +Blur = 7,7 +Low threshold = 36 diff --git a/testing/Camera/test_blurred.jpg b/testing/Camera/test_blurred.jpg new file mode 100644 index 0000000..78c484d Binary files /dev/null and b/testing/Camera/test_blurred.jpg differ diff --git a/testing/Camera/test_edge.jpg b/testing/Camera/test_edge.jpg new file mode 100644 index 0000000..9b076f8 Binary files /dev/null and b/testing/Camera/test_edge.jpg differ diff --git a/testing/Camera/test_edge_avg.jpg b/testing/Camera/test_edge_avg.jpg new file mode 100644 index 0000000..45ff0bd Binary files /dev/null and b/testing/Camera/test_edge_avg.jpg differ diff --git a/testing/Camera/testimage.jpg b/testing/Camera/testimage.jpg new file mode 100644 index 0000000..caaf9e6 Binary files /dev/null and b/testing/Camera/testimage.jpg differ diff --git a/testing/CansWebInterface/To Be Ammended/UWA_generic3.jpg b/testing/CansWebInterface/To Be Ammended/UWA_generic3.jpg new file mode 100644 index 0000000..4d45bb9 Binary files /dev/null and b/testing/CansWebInterface/To Be Ammended/UWA_generic3.jpg differ diff --git a/testing/CansWebInterface/To Be Ammended/mctx.gui2.js b/testing/CansWebInterface/To Be Ammended/mctx.gui2.js new file mode 100644 index 0000000..f779232 --- /dev/null +++ b/testing/CansWebInterface/To Be Ammended/mctx.gui2.js @@ -0,0 +1,199 @@ +/** + * MCTX3420 2013 GUI stuff. + */ + +mctx = {}; +mctx.api = location.protocol + "//" + location.host + "/api/"; +mctx.expected_api_version = 0; +mctx.key = undefined; +mctx.has_control = false; + +mctx.return_codes = { + "1" : "Ok", + "-1" : "General error", + "-2" : "Unauthorized", + "-3" : "Not running", + "-4" : "Already exists" +}; + +mctx.sensors = { + 0 : {name : "Strain gauge 1"}, + 1 : {name : "Strain gauge 2"}, + 2 : {name : "Strain gauge 3"}, + 3 : {name : "Strain gauge 4"}, + 4 : {name : "Pressure sensor 1"}, + 5 : {name : "Pressure sensor 2"} +}; + +mctx.actuators = { + 0 : {name : "Solenoid 1"}, + 1 : {name : "Solenoid 2"}, + 2 : {name : "Solenoid 3"}, + 3 : {name : "Pressure regulator"} +}; + +mctx.strain_gauges = {}; +mctx.strain_gauges.ids = [0, 1, 2, 3]; +mctx.strain_gauges.time_limit = 20; + +/** + * Writes the current date to wherever it's called. + */ +function getDate(){ + document.write((new Date()).toDateString()); +} + +/** + * Populates a submenu of the navigation bar + * @param {string} header The header + * @param {object} items An object representing the submenu items + * @param {function} translator A function that translates an object item + * into a text and href. + * @returns {$.fn} Itself + */ +$.fn.populateSubmenu = function(header, items, translator) { + var submenuHeader = $("
  • ").append($("", {text : header, href : "#"})); + var submenu = $("
      ", {"class" : "submenu"}); + + for (var item in items) { + var info = translator(item, items); + submenu.append($("
    • ").append( + $("", {text : info.text, + href : info.href, target : "_blank"}) + )); + } + + this.append(submenuHeader.append(submenu)); + return this; +}; + +/** + * Populates the navigation bar + */ +$.fn.populateNavbar = function () { + var menu = $("
        ", {"class" : "menu"}); + var sensorTranslator = function(item, items) { + var href = mctx.api + "sensors?start_time=0&format=tsv&id=" + item; + return {text : items[item].name, href : href}; + }; + var actuatorTranslator = function(item, items) { + var href = mctx.api + "actuators?start_time=0&format=tsv&id=" + item; + return {text : items[item].name, href : href}; + }; + + menu.populateSubmenu("Home", mctx.home, sensorTranslator); + menu.populateSubmenu("Setup", mctx.setup, actuatorTranslator); + menu.populateSubmenu("Admin", mctx.admin, actuatorTranslator); + menu.populateSubmenu("Help", mctx.help, actuatorTranslator); + menu.appendTo(this); + return this; +} + +/** + * Sets the camera autoupdater + * @returns {$.fn} + */ +$.fn.setCamera = function () { + var url = mctx.api + "image"; //http://beaglebone/api/image + var update = true; + + //Stop updating if we can't retrieve an image! + this.error(function() { + update = false; + }); + + var parent = this; + + var updater = function() { + if (!update) { + alert("Cam fail"); + parent.attr("src", ""); + return; + } + + parent.attr("src", url + "#" + (new Date()).getTime()); + + setTimeout(updater, 1000); + }; + + updater(); + return this; +}; + +$.fn.setStrainGraphs = function () { + var sensor_url = mctx.api + "sensors"; + var graphdiv = this; + + var updater = function () { + var time_limit = mctx.strain_gauges.time_limit; + var responses = new Array(mctx.strain_gauges.ids.length); + + for (var i = 0; i < mctx.strain_gauges.ids.length; i++) { + var parameters = {id : i, start_time: -time_limit}; + responses[i] = $.ajax({url : sensor_url, data : parameters}); + } + + $.when.apply(this, responses).then(function () { + var data = new Array(arguments.length); + for (var i = 0; i < arguments.length; i++) { + var raw_data = arguments[i][0].data; + var pruned_data = []; + var step = ~~(raw_data.length/100); + for (var j = 0; j < raw_data.length; j += step) + pruned_data.push(raw_data[j]); + data[i] = pruned_data; + } + $.plot(graphdiv, data); + setTimeout(updater, 500); + }, function () {alert("It crashed");}); + }; + + updater(); + return this; +}; + +$.fn.login = function () { + var username = this.find("input[name='username']").val(); + var password = this.find("input[name='pass']").val(); + var force = this.find("input[name='force']").is(":checked"); + var url = mctx.api + "control"; + + var authFunc = function(xhr) { + xhr.setRequestHeader("Authorization", + "Basic " + base64.encode(username + ":" + password)); + }; + + $.ajax({ + url : url, + data : {action : "lock", force : (force ? true : undefined)}, + beforeSend : authFunc + }).done(function (data) { + mctx.key = data.key; + if (data.status < 0) { + alert("no - " + data.description); + } else { + mctx.has_control = true; + alert("Login Successful - " + mctx.key); + } + }).fail(function (jqXHR) { + mctx.key = undefined; + mctx.has_control = false; + alert("Login Failed: Please ensure your pheme Username and Password are correct"); + }); +}; + +$.fn.setErrorLog = function () { + var url = mctx.api + "errorlog"; + var outdiv = this; + + var updater = function () { + $.ajax({url : url}).done(function (data) { + outdiv.text(data); + setTimeout(updater, 1000); + }).fail(function (jqXHR) { + outdiv.text("Failed to retrieve the error log."); + }); + }; + + updater(); +}; \ No newline at end of file diff --git a/testing/CansWebInterface/To Be Ammended/style.css b/testing/CansWebInterface/To Be Ammended/style.css new file mode 100644 index 0000000..7ebfee1 --- /dev/null +++ b/testing/CansWebInterface/To Be Ammended/style.css @@ -0,0 +1,166 @@ +/** Custom fonts **/ +@font-face { + font-family: "Open Sans"; + src: url("OpenSans.ttf"), + url("OpenSans.eot"); +} + +/** Limit max width and auto-center content **/ +html { + max-width: 1280px; + margin: 0 auto; +} + +body { + font-family: "Open Sans", "Lucida Grande","Lucida Sans",Verdana,Arial; + font-size: 13px; + background-color: #F5F5F5; +} + +hr { + border: 0; + border-bottom: 1px solid gray; +} + +table { + border: none; +} + +table.centre { + margin: auto; +} + +table.status, table.status tr, table.status td { + padding: 0.2em 0.75em; +} + +th { + padding: inherit; + border-bottom: 1px solid gray; +} + +img.centre { + display: block; + margin: auto; +} + +input[type="button"], input[type="submit"] { + background-color: #F5F5F5; + border: 1px solid #A2A2A2; + border-radius: 3px; + box-shadow: 1px 1px 1px #BBBBBB; + transition: all 0.13s ease 0s; +} + +input[type="button"]:active, input[type="submit"]:active { + background-color: #E8E8E8; +} + +/* IE8 width bugfix */ +input[type="text"], input[type="password"] { + width: 100%; +} + +#header { + background-color: #000000; + color: #f2f2f2; + padding: 1.5em 2em; + margin-top: -1em; + margin-bottom: 1em; + box-shadow: 0 0 0.5em #444444; + border-radius: 4px; + transition: all 0.2s ease 0s; +} + +#header #title { + font-family: "Open Sans", Arial; + font-size: 30px; +} + +#header #rightnav { + float: right; + padding-top: 0.5em; +} + +#header #menu-container { + margin-right: 1.5em; + font-size: 15px; + display: inline-block; +} + +#header #date { + padding-top: 1em; + font-size: 12px; +} + +#content { + padding: 1em 2em; +} + +#sidebar{ + float: right; + min-width: 22%; +} + +#sidebar .title { + font-size: 20px; + font-weight: bold; +} + +#sidebar .item { + padding: 0.2em; + margin-bottom: 0.5em; +} + +#main { + overflow: auto; + margin-right: 25%; + padding-right: 2em; + min-width: 400px; +} + +#main .title { + font-size: 24px; + font-weight: bold; + margin-bottom: 0.5em; +} + +.graph { + width: 100%; + height: 200px; +} + +.widget { + background-color: #EBF4FA; + margin: 0.25em 0.25em 1.5em; + padding: 1em 1.25em; + box-shadow: 0 0 3px #CCCCCC; + transition: all 0.2s ease 0s; + overflow: auto; +} + +.widget:hover { + box-shadow: 0 0 0.25em #AAAAAA; +} + +.widgetPanel { + background-color: #B4CFEC; + margin: 0.25em 0.25em 1.5em; + padding: 1em 1.25em; + box-shadow: 0 0 3px #CCCCCC; + transition: all 0.2s ease 0s; + overflow: auto; + /*color: #FFFFFF;*/ +} + +/** Hack **/ +.clear { + clear: both; +} + +#errorlog { + overflow: auto; + max-width: 100%; + width: 100%; + height: 6em; +} \ No newline at end of file diff --git a/testing/CansWebInterface/WidgetCode/controlWidget b/testing/CansWebInterface/WidgetCode/controlWidget new file mode 100644 index 0000000..24d6ba9 --- /dev/null +++ b/testing/CansWebInterface/WidgetCode/controlWidget @@ -0,0 +1,26 @@ +
        Controls
        + Main controls +
        + + + + + + + + + + + + + +
        Experiment name
        Experiment mode + Strain it + Explode it +
        + + + + +
        +
        diff --git a/testing/CansWebInterface/WidgetCode/statusPanel.html b/testing/CansWebInterface/WidgetCode/statusPanel.html new file mode 100644 index 0000000..67b170f --- /dev/null +++ b/testing/CansWebInterface/WidgetCode/statusPanel.html @@ -0,0 +1,13 @@ +
        +
        Status
        +
        + + + + + +
        Module State
        Server API PASS
        Enclosure interlock FAIL
        Pressure level PASS
        +
        + Software mode: off +
        +
        \ No newline at end of file diff --git a/testing/CansWebInterface/WidgetCode/widgetTEMPLATE.txt b/testing/CansWebInterface/WidgetCode/widgetTEMPLATE.txt new file mode 100644 index 0000000..4f1141a --- /dev/null +++ b/testing/CansWebInterface/WidgetCode/widgetTEMPLATE.txt @@ -0,0 +1,6 @@ +
        +
        ADD TITLE HERE
        +
        + ADD WIDGET CONTENT HERE +
        +
        \ No newline at end of file diff --git a/testing/CansWebInterface/current/Graph1.png b/testing/CansWebInterface/current/Graph1.png new file mode 100644 index 0000000..c8484b6 Binary files /dev/null and b/testing/CansWebInterface/current/Graph1.png differ diff --git a/testing/CansWebInterface/current/Graph2.png b/testing/CansWebInterface/current/Graph2.png new file mode 100644 index 0000000..9c73eb3 Binary files /dev/null and b/testing/CansWebInterface/current/Graph2.png differ diff --git a/testing/CansWebInterface/current/Graph3.png b/testing/CansWebInterface/current/Graph3.png new file mode 100644 index 0000000..6948a84 Binary files /dev/null and b/testing/CansWebInterface/current/Graph3.png differ diff --git a/testing/CansWebInterface/current/Graph4.png b/testing/CansWebInterface/current/Graph4.png new file mode 100644 index 0000000..26e9fd4 Binary files /dev/null and b/testing/CansWebInterface/current/Graph4.png differ diff --git a/testing/CansWebInterface/current/Graph5.png b/testing/CansWebInterface/current/Graph5.png new file mode 100644 index 0000000..2e36c98 Binary files /dev/null and b/testing/CansWebInterface/current/Graph5.png differ diff --git a/testing/CansWebInterface/current/Graph6.png b/testing/CansWebInterface/current/Graph6.png new file mode 100644 index 0000000..86fa22a Binary files /dev/null and b/testing/CansWebInterface/current/Graph6.png differ diff --git a/testing/CansWebInterface/current/all.zip b/testing/CansWebInterface/current/all.zip new file mode 100644 index 0000000..a12e55d Binary files /dev/null and b/testing/CansWebInterface/current/all.zip differ diff --git a/testing/CansWebInterface/current/dummy files to test download functionality.txt b/testing/CansWebInterface/current/dummy files to test download functionality.txt new file mode 100644 index 0000000..e69de29 diff --git a/testing/CansWebInterface/current/nograph.png b/testing/CansWebInterface/current/nograph.png new file mode 100644 index 0000000..018ba56 Binary files /dev/null and b/testing/CansWebInterface/current/nograph.png differ diff --git a/testing/CansWebInterface/current/pressure1 b/testing/CansWebInterface/current/pressure1 new file mode 100644 index 0000000..380f4ae --- /dev/null +++ b/testing/CansWebInterface/current/pressure1 @@ -0,0 +1,2 @@ +Would we be able to put files - like stream into the public - mctxweb file so that the cover page can access the file. +wasnt too sure how to go back files without going all the way back to my local filesystem diff --git a/testing/CansWebInterface/current/pressure2 b/testing/CansWebInterface/current/pressure2 new file mode 100644 index 0000000..380f4ae --- /dev/null +++ b/testing/CansWebInterface/current/pressure2 @@ -0,0 +1,2 @@ +Would we be able to put files - like stream into the public - mctxweb file so that the cover page can access the file. +wasnt too sure how to go back files without going all the way back to my local filesystem diff --git a/testing/CansWebInterface/current/strain1 b/testing/CansWebInterface/current/strain1 new file mode 100644 index 0000000..380f4ae --- /dev/null +++ b/testing/CansWebInterface/current/strain1 @@ -0,0 +1,2 @@ +Would we be able to put files - like stream into the public - mctxweb file so that the cover page can access the file. +wasnt too sure how to go back files without going all the way back to my local filesystem diff --git a/testing/CansWebInterface/current/strain2 b/testing/CansWebInterface/current/strain2 new file mode 100644 index 0000000..380f4ae --- /dev/null +++ b/testing/CansWebInterface/current/strain2 @@ -0,0 +1,2 @@ +Would we be able to put files - like stream into the public - mctxweb file so that the cover page can access the file. +wasnt too sure how to go back files without going all the way back to my local filesystem diff --git a/testing/CansWebInterface/current/strain3 b/testing/CansWebInterface/current/strain3 new file mode 100644 index 0000000..380f4ae --- /dev/null +++ b/testing/CansWebInterface/current/strain3 @@ -0,0 +1,2 @@ +Would we be able to put files - like stream into the public - mctxweb file so that the cover page can access the file. +wasnt too sure how to go back files without going all the way back to my local filesystem diff --git a/testing/CansWebInterface/current/strain4 b/testing/CansWebInterface/current/strain4 new file mode 100644 index 0000000..380f4ae --- /dev/null +++ b/testing/CansWebInterface/current/strain4 @@ -0,0 +1,2 @@ +Would we be able to put files - like stream into the public - mctxweb file so that the cover page can access the file. +wasnt too sure how to go back files without going all the way back to my local filesystem diff --git a/testing/CansWebInterface/dashboard.html b/testing/CansWebInterface/dashboard.html new file mode 100644 index 0000000..960d945 --- /dev/null +++ b/testing/CansWebInterface/dashboard.html @@ -0,0 +1,190 @@ + + + + MCTX3420 Web Interface + + + + + + + + + + + + + + +
        + +
        + +
        + + + +
        +
        +
        + +
        +
        +
        +
        System Diagram
        + System diagram + + Client PC + BBB + Electronics + Pneumatics + Sensors + Camera + Strain Can + Explode Can + Case + Mounting + Mounting Area + case Area + + + + +
        +
        +
        + + + + +
        + + diff --git a/testing/CansWebInterface/graph.html b/testing/CansWebInterface/graph.html new file mode 100644 index 0000000..350ed14 --- /dev/null +++ b/testing/CansWebInterface/graph.html @@ -0,0 +1,195 @@ + + + + MCTX3420 Web Interface + + + + + + + + + + + + + + + +
        + +
        + +
        + + + +
        + +
        +
        + +
        +
        + +
        +
        Graph
        +
        + +
        +
        +
        + + X-Axis +
        time
        + Y-Axis +
        + + +
        +
        +

        + Time of Last Update + Start Time + End Time +

        +
        +
        + + + + +
        + + +
        + + + + +
        + + diff --git a/testing/CansWebInterface/help.html b/testing/CansWebInterface/help.html new file mode 100644 index 0000000..6340cc6 --- /dev/null +++ b/testing/CansWebInterface/help.html @@ -0,0 +1,199 @@ + + + + MCTX3420 Web Interface + + + + + + + + + + + + + +
        + +
        + + +
        + + + + +
        +
        +
        Getting Started
        +

        Welcome to the MCTX3420 project documentation! On this page, you will find help on how to use the project software, + as well as information about the various hardware components.

        +

        For a basic introduction to using the experiment, see 'Tutorials'. + System overview provides a general overview of the experiment's hardware, while + Using the web interface provides a tutorial on using the the project software. + For detailed information about the experiment hardware, see the 'Hardware' section. For detailed information about the project software, + see the 'Software' section.

        +
        Tutorials
        + +
        Hardware
        + +
        Software
        + +
        + +
        +
        Project Wiki
        +

        All of the project's help documentation is stored in its GitHub wiki. This git repository also + stores all of the software code for the project; to access the project code files, visit this link.

        +

        The following is a list of pages on the GitHub wiki (all of which can be accessed via the categories above):

        +

        +
        +
        +
        + + + + + + + diff --git a/testing/CansWebInterface/index.html b/testing/CansWebInterface/index.html new file mode 100644 index 0000000..7061f8d --- /dev/null +++ b/testing/CansWebInterface/index.html @@ -0,0 +1,137 @@ + + + + MCTX3420 Web Interface + + + + + + + + + + + + + + + + +
        + + + +
        +
        +
        Dashboard
        + + Main controls +
        + + + + + + + + + + + + + +
        Experiment name
        Experiment mode + Strain it + Explode it +
        + + +
        +
        + Error and warning messages
        + +
        + +
        +
        Strain gauges
        +
        + +
        +
        + +
        +
        Camera Feed
        + Camera 1 +
        +
        + + +
        + + diff --git a/testing/CansWebInterface/index2.html b/testing/CansWebInterface/index2.html new file mode 100644 index 0000000..0846cf6 --- /dev/null +++ b/testing/CansWebInterface/index2.html @@ -0,0 +1,152 @@ + + + + MCTX3420 Web Interface + + + + + + + + + + + + + + +
        + +
        + +
        + + + +
        +
        +
        Welcome
        + + Introduction To Exploding Cans +

        + Welcome to the MCTX3420 remote pressure vessel experiment site! +

        +

        + To explore how this system works, hover over the elements of the + system diagram below. Clicking each element will lead to a new + page that briefly describes that component of the system. +

        +

        + For the full documentation, see the project wiki at: + + https://github.com/szmoore/MCTX3420/wiki + +

        + +

        + If you wish to learn more about the system or would like some assistance please click on the help link at the top of the page. This can be accessed at any time. + In order to access the Experiment Interface you must login below using your pheme username and password. +

        +
        + + +
        +
        Login
        +

        + Please use your pheme username and password. +

        +
        +
        + + + + + + + +
        Username
        Password
        + +
        +
        +
        +
        + +
        + + + + +
        + + diff --git a/testing/CansWebInterface/index3.html b/testing/CansWebInterface/index3.html new file mode 100644 index 0000000..3bccb8a --- /dev/null +++ b/testing/CansWebInterface/index3.html @@ -0,0 +1,153 @@ + + + + MCTX3420 Web Interface + + + + + + + + + + + + + + +
        + +
        + +
        + + + +
        +
        +
        Welcome
        + + Introduction To Exploding Cans +

        + Welcome to the MCTX3420 remote pressure vessel experiment site! +

        +

        + To explore how this system works, hover over the elements of the + system diagram below. Clicking each element will lead to a new + page that briefly describes that component of the system. +

        +

        + For the full documentation, see the project wiki at: + + https://github.com/szmoore/MCTX3420/wiki + +

        + +

        + If you wish to learn more about the system or would like some assistance please click on the help link at the top of the page. This can be accessed at any time. + In order to access the Experiment Interface you must login below using your pheme username and password. +

        +
        + + +
        +
        Login
        +

        + Please use your pheme username and password. +

        +
        +
        +

        + +

        +

        + +

        +

        + Forgotten password? +

        +

        + +

        +

        +   +

        +
        + +
        +
        + +
        + + + + +
        + + diff --git a/testing/CansWebInterface/static/OpenSans.eot b/testing/CansWebInterface/static/OpenSans.eot new file mode 100644 index 0000000..a0229f5 Binary files /dev/null and b/testing/CansWebInterface/static/OpenSans.eot differ diff --git a/testing/CansWebInterface/static/OpenSans.ttf b/testing/CansWebInterface/static/OpenSans.ttf new file mode 100644 index 0000000..959c9a4 Binary files /dev/null and b/testing/CansWebInterface/static/OpenSans.ttf differ diff --git a/testing/CansWebInterface/static/base64.js b/testing/CansWebInterface/static/base64.js new file mode 100644 index 0000000..72bced4 --- /dev/null +++ b/testing/CansWebInterface/static/base64.js @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2010 Nick Galbreath + * http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* base64 encode/decode compatible with window.btoa/atob + * + * window.atob/btoa is a Firefox extension to convert binary data (the "b") + * to base64 (ascii, the "a"). + * + * It is also found in Safari and Chrome. It is not available in IE. + * + * if (!window.btoa) window.btoa = base64.encode + * if (!window.atob) window.atob = base64.decode + * + * The original spec's for atob/btoa are a bit lacking + * https://developer.mozilla.org/en/DOM/window.atob + * https://developer.mozilla.org/en/DOM/window.btoa + * + * window.btoa and base64.encode takes a string where charCodeAt is [0,255] + * If any character is not [0,255], then an DOMException(5) is thrown. + * + * window.atob and base64.decode take a base64-encoded string + * If the input length is not a multiple of 4, or contains invalid characters + * then an DOMException(5) is thrown. + */ +var base64 = {}; +base64.PADCHAR = '='; +base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +base64.makeDOMException = function() { + // sadly in FF,Safari,Chrome you can't make a DOMException + var e, tmp; + + try { + return new DOMException(DOMException.INVALID_CHARACTER_ERR); + } catch (tmp) { + // not available, just passback a duck-typed equiv + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/prototype + var ex = new Error("DOM Exception 5"); + + // ex.number and ex.description is IE-specific. + ex.code = ex.number = 5; + ex.name = ex.description = "INVALID_CHARACTER_ERR"; + + // Safari/Chrome output format + ex.toString = function() { return 'Error: ' + ex.name + ': ' + ex.message; }; + return ex; + } +} + +base64.getbyte64 = function(s,i) { + // This is oddly fast, except on Chrome/V8. + // Minimal or no improvement in performance by using a + // object with properties mapping chars to value (eg. 'A': 0) + var idx = base64.ALPHA.indexOf(s.charAt(i)); + if (idx === -1) { + throw base64.makeDOMException(); + } + return idx; +} + +base64.decode = function(s) { + // convert to string + s = '' + s; + var getbyte64 = base64.getbyte64; + var pads, i, b10; + var imax = s.length + if (imax === 0) { + return s; + } + + if (imax % 4 !== 0) { + throw base64.makeDOMException(); + } + + pads = 0 + if (s.charAt(imax - 1) === base64.PADCHAR) { + pads = 1; + if (s.charAt(imax - 2) === base64.PADCHAR) { + pads = 2; + } + // either way, we want to ignore this last block + imax -= 4; + } + + var x = []; + for (i = 0; i < imax; i += 4) { + b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | + (getbyte64(s,i+2) << 6) | getbyte64(s,i+3); + x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff)); + } + + switch (pads) { + case 1: + b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6); + x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff)); + break; + case 2: + b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12); + x.push(String.fromCharCode(b10 >> 16)); + break; + } + return x.join(''); +} + +base64.getbyte = function(s,i) { + var x = s.charCodeAt(i); + if (x > 255) { + throw base64.makeDOMException(); + } + return x; +} + +base64.encode = function(s) { + if (arguments.length !== 1) { + throw new SyntaxError("Not enough arguments"); + } + var padchar = base64.PADCHAR; + var alpha = base64.ALPHA; + var getbyte = base64.getbyte; + + var i, b10; + var x = []; + + // convert to string + s = '' + s; + + var imax = s.length - s.length % 3; + + if (s.length === 0) { + return s; + } + for (i = 0; i < imax; i += 3) { + b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2); + x.push(alpha.charAt(b10 >> 18)); + x.push(alpha.charAt((b10 >> 12) & 0x3F)); + x.push(alpha.charAt((b10 >> 6) & 0x3f)); + x.push(alpha.charAt(b10 & 0x3f)); + } + switch (s.length - imax) { + case 1: + b10 = getbyte(s,i) << 16; + x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + + padchar + padchar); + break; + case 2: + b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8); + x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + + alpha.charAt((b10 >> 6) & 0x3f) + padchar); + break; + } + return x.join(''); +} + + diff --git a/testing/CansWebInterface/static/excanvas.min.js b/testing/CansWebInterface/static/excanvas.min.js new file mode 100644 index 0000000..fcf876c --- /dev/null +++ b/testing/CansWebInterface/static/excanvas.min.js @@ -0,0 +1 @@ +if(!document.createElement("canvas").getContext){(function(){var ab=Math;var n=ab.round;var l=ab.sin;var A=ab.cos;var H=ab.abs;var N=ab.sqrt;var d=10;var f=d/2;var z=+navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];function y(){return this.context_||(this.context_=new D(this))}var t=Array.prototype.slice;function g(j,m,p){var i=t.call(arguments,2);return function(){return j.apply(m,i.concat(t.call(arguments)))}}function af(i){return String(i).replace(/&/g,"&").replace(/"/g,""")}function Y(m,j,i){if(!m.namespaces[j]){m.namespaces.add(j,i,"#default#VML")}}function R(j){Y(j,"g_vml_","urn:schemas-microsoft-com:vml");Y(j,"g_o_","urn:schemas-microsoft-com:office:office");if(!j.styleSheets.ex_canvas_){var i=j.createStyleSheet();i.owningElement.id="ex_canvas_";i.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}R(document);var e={init:function(i){var j=i||document;j.createElement("canvas");j.attachEvent("onreadystatechange",g(this.init_,this,j))},init_:function(p){var m=p.getElementsByTagName("canvas");for(var j=0;j1){m--}if(6*m<1){return j+(i-j)*6*m}else{if(2*m<1){return i}else{if(3*m<2){return j+(i-j)*(2/3-m)*6}else{return j}}}}var C={};function F(j){if(j in C){return C[j]}var ag,Z=1;j=String(j);if(j.charAt(0)=="#"){ag=j}else{if(/^rgb/.test(j)){var p=M(j);var ag="#",ah;for(var m=0;m<3;m++){if(p[m].indexOf("%")!=-1){ah=Math.floor(c(p[m])*255)}else{ah=+p[m]}ag+=k[r(ah,0,255)]}Z=+p[3]}else{if(/^hsl/.test(j)){var p=M(j);ag=I(p);Z=p[3]}else{ag=b[j]||j}}}return C[j]={color:ag,alpha:Z}}var o={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var L={};function E(i){if(L[i]){return L[i]}var p=document.createElement("div");var m=p.style;try{m.font=i}catch(j){}return L[i]={style:m.fontStyle||o.style,variant:m.fontVariant||o.variant,weight:m.fontWeight||o.weight,size:m.fontSize||o.size,family:m.fontFamily||o.family}}function u(m,j){var i={};for(var ah in m){i[ah]=m[ah]}var ag=parseFloat(j.currentStyle.fontSize),Z=parseFloat(m.size);if(typeof m.size=="number"){i.size=m.size}else{if(m.size.indexOf("px")!=-1){i.size=Z}else{if(m.size.indexOf("em")!=-1){i.size=ag*Z}else{if(m.size.indexOf("%")!=-1){i.size=(ag/100)*Z}else{if(m.size.indexOf("pt")!=-1){i.size=Z/0.75}else{i.size=ag}}}}}i.size*=0.981;return i}function ac(i){return i.style+" "+i.variant+" "+i.weight+" "+i.size+"px "+i.family}var s={butt:"flat",round:"round"};function S(i){return s[i]||"square"}function D(i){this.m_=B();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=d*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var m="width:"+i.clientWidth+"px;height:"+i.clientHeight+"px;overflow:hidden;position:absolute";var j=i.ownerDocument.createElement("div");j.style.cssText=m;i.appendChild(j);var p=j.cloneNode(false);p.style.backgroundColor="red";p.style.filter="alpha(opacity=0)";i.appendChild(p);this.element_=j;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var q=D.prototype;q.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};q.beginPath=function(){this.currentPath_=[]};q.moveTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"moveTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.lineTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"lineTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.bezierCurveTo=function(m,j,ak,aj,ai,ag){var i=V(this,ai,ag);var ah=V(this,m,j);var Z=V(this,ak,aj);K(this,ah,Z,i)};function K(i,Z,m,j){i.currentPath_.push({type:"bezierCurveTo",cp1x:Z.x,cp1y:Z.y,cp2x:m.x,cp2y:m.y,x:j.x,y:j.y});i.currentX_=j.x;i.currentY_=j.y}q.quadraticCurveTo=function(ai,m,j,i){var ah=V(this,ai,m);var ag=V(this,j,i);var aj={x:this.currentX_+2/3*(ah.x-this.currentX_),y:this.currentY_+2/3*(ah.y-this.currentY_)};var Z={x:aj.x+(ag.x-this.currentX_)/3,y:aj.y+(ag.y-this.currentY_)/3};K(this,aj,Z,ag)};q.arc=function(al,aj,ak,ag,j,m){ak*=d;var ap=m?"at":"wa";var am=al+A(ag)*ak-f;var ao=aj+l(ag)*ak-f;var i=al+A(j)*ak-f;var an=aj+l(j)*ak-f;if(am==i&&!m){am+=0.125}var Z=V(this,al,aj);var ai=V(this,am,ao);var ah=V(this,i,an);this.currentPath_.push({type:ap,x:Z.x,y:Z.y,radius:ak,xStart:ai.x,yStart:ai.y,xEnd:ah.x,yEnd:ah.y})};q.rect=function(m,j,i,p){this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath()};q.strokeRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.stroke();this.currentPath_=Z};q.fillRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.fill();this.currentPath_=Z};q.createLinearGradient=function(j,p,i,m){var Z=new U("gradient");Z.x0_=j;Z.y0_=p;Z.x1_=i;Z.y1_=m;return Z};q.createRadialGradient=function(p,ag,m,j,Z,i){var ah=new U("gradientradial");ah.x0_=p;ah.y0_=ag;ah.r0_=m;ah.x1_=j;ah.y1_=Z;ah.r1_=i;return ah};q.drawImage=function(aq,m){var aj,ah,al,ay,ao,am,at,aA;var ak=aq.runtimeStyle.width;var ap=aq.runtimeStyle.height;aq.runtimeStyle.width="auto";aq.runtimeStyle.height="auto";var ai=aq.width;var aw=aq.height;aq.runtimeStyle.width=ak;aq.runtimeStyle.height=ap;if(arguments.length==3){aj=arguments[1];ah=arguments[2];ao=am=0;at=al=ai;aA=ay=aw}else{if(arguments.length==5){aj=arguments[1];ah=arguments[2];al=arguments[3];ay=arguments[4];ao=am=0;at=ai;aA=aw}else{if(arguments.length==9){ao=arguments[1];am=arguments[2];at=arguments[3];aA=arguments[4];aj=arguments[5];ah=arguments[6];al=arguments[7];ay=arguments[8]}else{throw Error("Invalid number of arguments")}}}var az=V(this,aj,ah);var p=at/2;var j=aA/2;var ax=[];var i=10;var ag=10;ax.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",ax.join(""))};q.stroke=function(ao){var Z=10;var ap=10;var ag=5000;var ai={x:null,y:null};var an={x:null,y:null};for(var aj=0;ajan.x){an.x=m.x}if(ai.y==null||m.yan.y){an.y=m.y}}}am.push(' ">');if(!ao){w(this,am)}else{G(this,am,ai,an)}am.push("");this.element_.insertAdjacentHTML("beforeEnd",am.join(""))}};function w(m,ag){var j=F(m.strokeStyle);var p=j.color;var Z=j.alpha*m.globalAlpha;var i=m.lineScale_*m.lineWidth;if(i<1){Z*=i}ag.push("')}function G(aq,ai,aK,ar){var aj=aq.fillStyle;var aB=aq.arcScaleX_;var aA=aq.arcScaleY_;var j=ar.x-aK.x;var p=ar.y-aK.y;if(aj instanceof U){var an=0;var aF={x:0,y:0};var ax=0;var am=1;if(aj.type_=="gradient"){var al=aj.x0_/aB;var m=aj.y0_/aA;var ak=aj.x1_/aB;var aM=aj.y1_/aA;var aJ=V(aq,al,m);var aI=V(aq,ak,aM);var ag=aI.x-aJ.x;var Z=aI.y-aJ.y;an=Math.atan2(ag,Z)*180/Math.PI;if(an<0){an+=360}if(an<0.000001){an=0}}else{var aJ=V(aq,aj.x0_,aj.y0_);aF={x:(aJ.x-aK.x)/j,y:(aJ.y-aK.y)/p};j/=aB*d;p/=aA*d;var aD=ab.max(j,p);ax=2*aj.r0_/aD;am=2*aj.r1_/aD-ax}var av=aj.colors_;av.sort(function(aN,i){return aN.offset-i.offset});var ap=av.length;var au=av[0].color;var at=av[ap-1].color;var az=av[0].alpha*aq.globalAlpha;var ay=av[ap-1].alpha*aq.globalAlpha;var aE=[];for(var aH=0;aH')}else{if(aj instanceof T){if(j&&p){var ah=-aK.x;var aC=-aK.y;ai.push("')}}else{var aL=F(aq.fillStyle);var aw=aL.color;var aG=aL.alpha*aq.globalAlpha;ai.push('')}}}q.fill=function(){this.stroke(true)};q.closePath=function(){this.currentPath_.push({type:"close"})};function V(j,Z,p){var i=j.m_;return{x:d*(Z*i[0][0]+p*i[1][0]+i[2][0])-f,y:d*(Z*i[0][1]+p*i[1][1]+i[2][1])-f}}q.save=function(){var i={};v(this,i);this.aStack_.push(i);this.mStack_.push(this.m_);this.m_=J(B(),this.m_)};q.restore=function(){if(this.aStack_.length){v(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function h(i){return isFinite(i[0][0])&&isFinite(i[0][1])&&isFinite(i[1][0])&&isFinite(i[1][1])&&isFinite(i[2][0])&&isFinite(i[2][1])}function aa(j,i,p){if(!h(i)){return}j.m_=i;if(p){var Z=i[0][0]*i[1][1]-i[0][1]*i[1][0];j.lineScale_=N(H(Z))}}q.translate=function(m,j){var i=[[1,0,0],[0,1,0],[m,j,1]];aa(this,J(i,this.m_),false)};q.rotate=function(j){var p=A(j);var m=l(j);var i=[[p,m,0],[-m,p,0],[0,0,1]];aa(this,J(i,this.m_),false)};q.scale=function(m,j){this.arcScaleX_*=m;this.arcScaleY_*=j;var i=[[m,0,0],[0,j,0],[0,0,1]];aa(this,J(i,this.m_),true)};q.transform=function(Z,p,ah,ag,j,i){var m=[[Z,p,0],[ah,ag,0],[j,i,1]];aa(this,J(m,this.m_),true)};q.setTransform=function(ag,Z,ai,ah,p,j){var i=[[ag,Z,0],[ai,ah,0],[p,j,1]];aa(this,i,true)};q.drawText_=function(am,ak,aj,ap,ai){var ao=this.m_,at=1000,j=0,ar=at,ah={x:0,y:0},ag=[];var i=u(E(this.font),this.element_);var p=ac(i);var au=this.element_.currentStyle;var Z=this.textAlign.toLowerCase();switch(Z){case"left":case"center":case"right":break;case"end":Z=au.direction=="ltr"?"right":"left";break;case"start":Z=au.direction=="rtl"?"right":"left";break;default:Z="left"}switch(this.textBaseline){case"hanging":case"top":ah.y=i.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":ah.y=-i.size/2.25;break}switch(Z){case"right":j=at;ar=0.05;break;case"center":j=ar=at/2;break}var aq=V(this,ak+ah.x,aj+ah.y);ag.push('');if(ai){w(this,ag)}else{G(this,ag,{x:-j,y:0},{x:ar,y:i.size})}var an=ao[0][0].toFixed(3)+","+ao[1][0].toFixed(3)+","+ao[0][1].toFixed(3)+","+ao[1][1].toFixed(3)+",0,0";var al=n(aq.x/d)+","+n(aq.y/d);ag.push('','','');this.element_.insertAdjacentHTML("beforeEnd",ag.join(""))};q.fillText=function(m,i,p,j){this.drawText_(m,i,p,j,false)};q.strokeText=function(m,i,p,j){this.drawText_(m,i,p,j,true)};q.measureText=function(m){if(!this.textMeasureEl_){var i='';this.element_.insertAdjacentHTML("beforeEnd",i);this.textMeasureEl_=this.element_.lastChild}var j=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(j.createTextNode(m));return{width:this.textMeasureEl_.offsetWidth}};q.clip=function(){};q.arcTo=function(){};q.createPattern=function(j,i){return new T(j,i)};function U(i){this.type_=i;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}U.prototype.addColorStop=function(j,i){i=F(i);this.colors_.push({offset:j,color:i.color,alpha:i.alpha})};function T(j,i){Q(j);switch(i){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=i;break;default:O("SYNTAX_ERR")}this.src_=j.src;this.width_=j.width;this.height_=j.height}function O(i){throw new P(i)}function Q(i){if(!i||i.nodeType!=1||i.tagName!="IMG"){O("TYPE_MISMATCH_ERR")}if(i.readyState!="complete"){O("INVALID_STATE_ERR")}}function P(i){this.code=this[i];this.message=i+": DOM Exception "+this.code}var X=P.prototype=new Error;X.INDEX_SIZE_ERR=1;X.DOMSTRING_SIZE_ERR=2;X.HIERARCHY_REQUEST_ERR=3;X.WRONG_DOCUMENT_ERR=4;X.INVALID_CHARACTER_ERR=5;X.NO_DATA_ALLOWED_ERR=6;X.NO_MODIFICATION_ALLOWED_ERR=7;X.NOT_FOUND_ERR=8;X.NOT_SUPPORTED_ERR=9;X.INUSE_ATTRIBUTE_ERR=10;X.INVALID_STATE_ERR=11;X.SYNTAX_ERR=12;X.INVALID_MODIFICATION_ERR=13;X.NAMESPACE_ERR=14;X.INVALID_ACCESS_ERR=15;X.VALIDATION_ERR=16;X.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=e;CanvasRenderingContext2D=D;CanvasGradient=U;CanvasPattern=T;DOMException=P})()}; \ No newline at end of file diff --git a/testing/CansWebInterface/static/exp-menu.css b/testing/CansWebInterface/static/exp-menu.css new file mode 100644 index 0000000..6d1cbd2 --- /dev/null +++ b/testing/CansWebInterface/static/exp-menu.css @@ -0,0 +1,89 @@ +/* + Document : nav-menu + Created on : 21/09/2013, 8:33:30 PM + Author : Jeremy Tan Ammended By James Rosher + Description: + Navigation menu styling. + Background colour: #2A2A2A + Highlight colour: #f2f2f2 + Normal text colour: #bbbbbb +*/ + +/** Easing **/ +.exp-menu { + transition: all 0.2s ease 0s; +} + +/** Main menu list styling **/ +.exp-menu ul { + list-style: none outside none; + margin: 0; + padding-left: 0; + text-align: center; +} + +/** Menu item styling **/ +.exp-menu li { + display: inline-block; + position: relative; +} + +/** Link styling **/ +.exp-menu a { + background-color: #3090C7; + border-radius: 2px; + border: 1px solid #F0FFFF; + color: #FFFFFF; + display: inline-block; + margin: 0.1em; + padding: 0.2em 2em; + text-decoration: none; + transition: all 0.2s ease 0s; +} + +/** Highlight currently hovered-over item **/ +.exp-menu li:hover > a { + background-color: #87AFC7; + color: #2a2a2a; +} + +/** Display submenu when hovering over list item **/ +.exp-menu ul li:hover > ul { + display: block; +} + +/** Submenu styling **/ +.exp-menu ul ul { + background-color: #2A2A2A; + border-radius: 2px; + box-shadow: 1px 1px 1px #2A2A2A; + display: none; + left: 0; + padding: 0.25em 0.5em; + position: absolute; + text-align: center; + z-index: 10; +} + +/** Submenu link styling **/ +.exp-menu ul ul a { + width: 11em; + padding: 0.25em 0.5em; +} + +/** + Style of menu: +
        + +
        +*/ \ No newline at end of file diff --git a/testing/CansWebInterface/static/jquery-1.10.1.min.js b/testing/CansWebInterface/static/jquery-1.10.1.min.js new file mode 100644 index 0000000..e407e76 --- /dev/null +++ b/testing/CansWebInterface/static/jquery-1.10.1.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.1.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.1",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=lt(),k=lt(),E=lt(),S=!1,A=function(){return 0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=bt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+xt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return At(e.replace(z,"$1"),t,n,i)}function st(e){return K.test(e+"")}function lt(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[b]=!0,e}function ct(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function pt(e,t,n){e=e.split("|");var r,i=e.length,a=n?null:t;while(i--)(r=o.attrHandle[e[i]])&&r!==t||(o.attrHandle[e[i]]=a)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function dt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function gt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function yt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function vt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.parentWindow;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.frameElement&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ct(function(e){return e.innerHTML="",pt("type|href|height|width",dt,"#"===e.firstChild.getAttribute("href")),pt(B,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),r.input=ct(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),pt("value",ht,r.attributes&&r.input),r.getElementsByTagName=ct(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ct(function(e){return e.innerHTML="
        ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ct(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=st(n.querySelectorAll))&&(ct(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ct(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=st(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ct(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=st(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},r.sortDetached=ct(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return gt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?gt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:ut,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=bt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?ut(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return at(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:vt(function(){return[0]}),last:vt(function(e,t){return[t-1]}),eq:vt(function(e,t,n){return[0>n?n+t:n]}),even:vt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:vt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:vt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:vt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=mt(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=yt(n);function bt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function wt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function Tt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ct(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function Nt(e,t,n,r,i,o){return r&&!r[b]&&(r=Nt(r)),i&&!i[b]&&(i=Nt(i,o)),ut(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||St(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:Ct(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=Ct(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=Ct(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function kt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=wt(function(e){return e===t},s,!0),p=wt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[wt(Tt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return Nt(l>1&&Tt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),i>r&&kt(e=e.slice(r)),i>r&&xt(e))}f.push(n)}return Tt(f)}function Et(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=Ct(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?ut(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=bt(e)),n=t.length;while(n--)o=kt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Et(i,r))}return o};function St(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function At(e,t,n,i){var a,s,u,c,p,f=bt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&xt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}o.pseudos.nth=o.pseudos.eq;function jt(){}jt.prototype=o.filters=o.pseudos,o.setFilters=new jt,r.sortStable=b.split("").sort(A).join("")===b,p(),[0,0].sort(A),r.detectDuplicates=S,x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!u||(n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
        a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
        t
        ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
        ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null) +}),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=x(this),l=t,u=e.match(T)||[];while(o=u[a++])l=r?l:!s.hasClass(o),s[l?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
        ","
        "],area:[1,"",""],param:[1,"",""],thead:[1,"","
        "],tr:[2,"","
        "],col:[2,"","
        "],td:[3,"","
        "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
        ","
        "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("