Monday, January 7, 2008

Adventures with a Roomba 540

Okay, Okay, I know this isn't REALLY about semantics, but I just bought a Roomba 540 on the weekend and it has been such a great adventure learning how it "thinks" that I want to share my adventures with everyone!

I have a 650 sqft apartment. This should be a piece of cake for a little vacuum robot that is advertised as being capable of managing four rooms... let's see, shall we?

Day 1: I hit "start". The little robot makes a "the robot is happy!" noise and starts to explore my apartment. It first gets tangled in my television cable wire and spins itself around a few times, but is successful in getting itself untangled - this is as advertised, so well done! It then gets tangled in the speaker wires that are (to my shagrin and frustration) laying across my living room floor because my landlord wont let me cut the carpet to bury them. It, again, untangles itself perfectly and continues on its merry way. It seems that it really does understand what's happening because even after getting twisted and self-rotated in these wires it still "anticipates" walls (if you have never seen these robots work, they clearly have a map of the room in their heads because they will slow-down as they approach a wall such that they nudge it ever-so-gently before turning around. But alas, my Ikea furniture was the death of this little robot... I have two Pella Chairs (http://www.ikea.com/PIAimages/38296_PE130209_S4.jpg) from Ikea. The base of these chairs are, effectively, three wooden planks in a 3/4 square. The robot climbed half-way over the plank, and then was helplessly unbalanced and couldn't get either wheel on the floor to extract itself. It made the "I'm an unhappy little robot" noise, and died. ...Recharge...

Day 2: Same story. Same result. my little robot was helplessly danging on the plank of an Ikea chair. so I lifted it up and moved it 6 inches so that it could get a grip and then restarted it. It made the "happy little robot" sound and ran around cleaning my living room; however somehow it was unable to find its base-station again for charging. It came oh so very close! Within about 6 inches, amusingly... Recharge...

Day 3: This time it seems that it decided to visit the bathroom before fully exploring the living room... and got hung-up on the 2'X3' shag shower mat... unhappy little robot.... Recharge...

Day 4: I decide to tip the Pella chairs over so that the planks are no longer on the floor. THIS time it happily navigates around the chairs and cleans about half of the living room before deciding to wander off and explore the corridor. The corridor leads to my bedroom. It wandered into my bedroom and had a wonderful time in there dashing around under the bed cleaning all sorts of things that I really don't want to know... but it couldn't find it's way out again! Every time it came close to the exit doorway, it hesitated (as if there were a wall there) and turned around and went back in. I watched it do this 7 times!! (my bedroom carpet is now cleaner than it has ever been!). Finally I decided to teach it a lesson and I smaked it's nose every time it turned the wrong way. After a good many smacks (it was absolutely DETERMINED not to go out of the exit!! it would forcefully turn around even when it was half-way out the door!!) I finally got it back into the corridor. Once in the corridor it was happy again, and even anticipated the walls! This surprised me since I must have totally buggered-up it's map my smacking it in the nose so often... nevertheless, it navigated it's way back down the corridor and into the living room. It anticipated the overturned chairs, and then went on to re-program my digital cable box, which is sitting at ground level, and has all of it's buttons exposed to the little robot's bumble-bee-on-a-window bounces as it explores obsticles. I was about to give-up hope when, suddenly, it started to move quite aggressively around a path and directly back to it's charger!! It oriented itself and drove straight into it's charger - perfectly aligned - and stopped for the day!

I suspect that my experience is very similar to what new parents feel when they have to child-proof their apartments... I now know that I have to overturn my chairs (oh, and I have to lift my computer table off of the ground, because it is just a half-centimeter higher than the edges of the robot, and when it hits the underside of the table at full-speed it becomes nicely jammed underneath!)

So I have a half-clean carpet in my living room, a completely clean carpet in my bedroom, and a robot that finally navigated my house without making any "unhappy robot" noises. I think I'm close to having a clean house every day!

Thursday, November 22, 2007

A more complex LSID client

This one merges metadata from multiple endpoints, and then parses it using RDF::Core.


use strict;
use LS::ID;
use LS::Locator;
use RDF::Core::Model::Parser;
use RDF::Core::Storage::Memory;
use RDF::Core::Model;
use RDF::Core::Resource;
use RDF::Core::Literal;
use RDF::Core::Statement;

my $storage = new RDF::Core::Storage::Memory;
my $model = new RDF::Core::Model (Storage => $storage);
my $id = 'urn:lsid:biomoby.org:serviceinstance:bioinfo.icapture.ubc.ca,FASTA2HighestGenericSequenceObject:2006-04-12T18-27-15Z';

# the lines below convert the LSID into a URL since that's what BioMoby
# currently returns in its metadata... sorry!
my $r = 'http://biomoby.org/RESOURCES/MOBY-S/ServiceInstances#';
$id =~ /urn:lsid:biomoby.org:serviceinstance:([^:]+)/;
$r .= $1;

my $lsid = LS::ID->new($id);

my $locator = LS::Locator->new();
my $authority = $locator->resolveAuthority($lsid);
my $resource = $authority->getResource($lsid);
my $locations = $resource->getMetadataLocations;

foreach my $locs(keys %$locations){
foreach my $loc(@{$locations->{$locs}}){

my $data;
eval{$data = $resource->getMetadata(location => $loc);};
next if $@;

my $response_filehandle = $data->response;
my $RDF = join "", <$response_filehandle>;

my %options = (Model => $model,
Source => $RDF,
SourceType => 'string',
BaseURI => "http://www.foo.com/",
BNodePrefix => "genid"
);
my $parser = new RDF::Core::Model::Parser(%options);
$parser->parse;


}
}
print "model contains ", $model->countStmts, " statements\n\n";
my $resource = RDF::Core::Resource->new($r);
my $enumerator = $model->getStmts($resource);
my $statement = $enumerator->getFirst;
while (defined $statement) {
my $s= $statement->getSubject->getLocalValue;
my $o= $statement->getObject;
$o=($o->isLiteral?$o->getValue:$o->getLocalValue);
my $p= $statement->getPredicate->getLocalValue;
print "$s $p $o\n" unless $o =~ /genid/;
$statement = $enumerator->getNext
}
$enumerator->close;

Wednesday, November 14, 2007

LSID Perl libraries submitted to CPAN

Just a quick note to announce that my lead developer, Eddie Kawas, has recently uploaded the Perl LSID stack to CPAN. As such, it is no longer necessary to get the code from the sourceforge Subversion repository. Just start-up CPAN and say "install LS".

Google Gadgets for BioMoby


I love it when a bunch of open APIs come together into something greater than the sum of the parts :-)

Thanks to the good folks at Google and their Google Gadgets for making things so darn simple! It only took an hour or so to write a script that wrapped every BioMoby Web service (more than 1400 of them!) with a Google Gadget. Now it is an absolute a no-brainer to create an amazing mashup of bioinformatics data by simply selecting the various services you are interested in and dropping their Gadgets on your iGoogle page. You might even make a variety of different Gadget tabs with different combinations of services to provide different views.

I'm still working on getting the rendering to be more size-appropriate (currently I am using the same rendering engine as I used in Gbrowse_moby) but I think it's pretty good for a first-pass!

Try them for yourself: http://mobycentral.icapture.ubc.ca/Gadgets

Thursday, August 30, 2007

An LSID client

The last post was on how to build a resolver. This post is on how to build a client. The simple Perl client below retrieves metadata only (but I think it's fairly obvious which line needs to be changed to retrieve data ;-) )

Here we go!


use LS::ID;
use LS::Locator;

$lsid = LS::ID->new(
'urn:lsid:biomoby.org:servicetype:Retrieval:2001-09-21T16-00-00Z'
);

$locator = LS::Locator->new();
$authority = $locator->resolveAuthority($lsid);
$resource = $authority->getResource($lsid);
$data = $resource->getMetadata();
$response_filehandle = $data->response;
print <$response_filehandle>;
print "\n";


Voila! You hopefully have a bunch of RDF on your screen right now. Sure, that's a couple more lines than LWP::Simple, but some of that could be better encapsulated, so it really is only one or two lines more code than trying to do a GET... and with all sorts of benefits!

Oh! And here's how you get the LSID code from sourceforge. Get yourself a copy of Subversion, then cleck-out the code using subversion as so:

svn co http://lsids.svn.sourceforge.net/svnroot/lsids/trunk/lsid-perl lsids

(windows users will have a nice GUI for this)

Note that the sourceforge site indicates that it should be https://, but it works just as well as http, so you don't have to re-install Subversion to make it SSL compliant.

Wednesday, August 29, 2007

The Hello World LSID authority and metadata server

Since I'm out there advocating for LSIDs, and since the documentaton for LSIDs is hard to find and hard to work-through, I thought I'd take a stab at doing a "Hello World" LSID authority and metadata resolution server.

This is "as easy as it gets", but the process can be much more complex if needed. This solution requires no modification of the DNS/SRV records, and runs as a simple HTTP GET CGI service for metadata resolution.

Here we go! :-)


*** A HELLO WORLD LSID AUTHORITY ***
*** authority.pl ***

#!/usr/bin/perl
use strict;
use warnings;


use LS::Service::Authority;
use LS::Service::DataService;
use LS::HTTP::Service;
use LS::SOAP::Service transport=> 'HTTP::CGI';

my $location = 'http://';
if($ENV{'HTTP_HOST'} ) {
$location .= $ENV{'HTTP_HOST'};
}
else {
$location .= 'localhost:8080';
}


# FIRST create the authority service

my $authority_service = LS::Service::Authority->new(
name=> 'Hello_World',
authority=> 'bioinfo.icapture.ubc.ca',
location=> $location);


# SECOND create an HTTP GET metadata port
# what comes in here will be:
# $location/authority/metadata?lsid=URN:LSID:blah.blah:nspace:id:version

my $metadata_port =
LS::Authority::WSDL::Simple::MetadataPort->newMetadata(
portName=> 'Hello_there_World',
endpoint=> "$location/authority/metadata",
protocol=> $LS::Authority::WSDL::Constants::Protocols::HTTP,
);
$authority_service->addPort(
serviceName=> 'Hello_World',
port=> $metadata_port);


my $authority_server = LS::SOAP::Service->new();
$authority_server->authorityService($authority_service);

my $http_authority = LS::HTTP::Service->new();
$http_authority->dispatch_authority_to($authority_service);

$authority_server->httpServer($http_authority);
$authority_server->dispatch();



***** HELLO WORLD LSID METADATA SERVER *****
***** metadata.pl ******

#!/usr/bin/perl

use CGI qw/:all/;
my $C = CGI->new;

use strict;
use warnings;

use LS::ID;
use LS::Service::Response;
use LS::Service::Fault;
use LS::RDF::SimpleDocument;

my $lsid = param('lsid');
my $format = param('format') || "text/xml";

print header(-type => $format);
print getMetadata($lsid)->response;

sub getMetadata {
my ($lsid, $format) = @_;
$lsid = LS::ID->new($lsid);
$lsid = $lsid->canonical();

my $id = $lsid->object();
$id .= ':' . $lsid->revision() if($lsid->revision());

return LS::Service::Fault->serverFault(
'Can not do what I cannot do', 500)
unless($id);

# Create a new RDF Document to add triples

my $rdfDoc = LS::RDF::SimpleDocument->new();

return LS::Service::Fault->serverFault(
'Internal error, unable to initialize RDF document', 500)
unless($rdfDoc);

return LS::Service::Fault->fault('Unknown LSID')
unless(1); # whatever conditions you want...


$rdfDoc->addTripleLiteral(
$lsid->as_string(),
'http://purl.org/dc/elements/1.1/#title',
"Hello World");

$rdfDoc->addTripleResource(
$lsid->as_string(),
'urn:lsid:example.com:predicates:another_lsid',
'urn:lsid:biomoby.org:objectclass:DNASequence');

$format = 'application/xml' if(!$format);
return LS::Service::Response->new(
response=> '' .
$rdfDoc->output(),
format=> $format);
}



***** APACHE Server Config ******
***** add these lines to your httpd.conf file ****


ScriptAlias /authority/metadata "/usr/local/apache2/LSID/metadata.pl"
ScriptAlias /authority "/usr/local/apache2/LSID/authority.pl"

<Directory /usr/local/apache2/LSID>
Options ExecCGI
Order allow,deny
Allow from all
</Directory>


*** now put your files in the right place

Create a folder /usr/local/apache2/LSID/
save authority.pl to that folder
save metadata.pl to that folder
(get the permissions right!)

DONE!

You now have an LSID authority and metadata resolver! Enjoy!

Saturday, July 7, 2007

More arguments for the LSID

I just realized that I forgot to mention the other way that the BioMoby project utilizes LSIDs.

The LSID spec includes versioning!

Our client software can, simply by string-comparison of LSIDs, detect when a BioMoby service has changed! The time-stamp that is appended to the LSID representing a service is updated for every change that the service-provider makes. This time-stamp is included in the "version" field of every LSID, so that every client program knows if it is looking at the same Web Service, or a modified one.

Again... let's see you do THAT using URLs! (without contorting yourself like a rubber-band man)

I state once more, that URLs are NOT the solution to the semantic web!

Let's get over it and move on!

Mark