Tuesday, October 27, 2009

Smallworld Camp

As I was returning from the GE Smallworld Americas Users Conference last week, I began to reflect on the pros and cons of my experience there. A highlight of the trip was the networking I was able to do with colleagues, customers, potential customers, etc. I was able to sit in on a number of presentations, too. It turned out that the ones that I found most interesting were the ones that had some kind of live demo or at the very least some code/pseudocode that showed how a particular problem was solved.

So it seems that I have a greater attention span for technically detailed presentations than I do for high-level marketing presentations. And that got me thinking about what options were available for some kind of different way of having technical interactions in the Smallworld/Magik realm than the typical conference presentation format. My research "found" that there is exactly that kind of "different" format available in the wider coding/techie world. It goes by names such as FooCamp, BarCamp, CodeCamp, unconference.

As I read more about it, I thought "why not a Smallworld Camp?" My colleague Graham and I get together for "coffee and code" sessions where the content is completely participant-driven. And I always get alot out of those sessions. How difficult would it be to coordinate a Smallworld Camp if its participants were all motivated participants? I have not heard of any kind of Smallworld/Magik type of BarCamp. If anyone has participated in such an unconference, please let me know. I would love to hear how it went.

While waiting to hear if any other such participant-driven events have been planned, I present to you Smallworld Camp. I have borrowed heavily from the BarCamp.org wiki template. All BarCamps are about open sharing and education. A Smallworld Camp is about this open interaction within the Smallworld developer community. Having an accessible repository for presentation documents and demo code contributes to that openness.

Why participate in a Smallworld Camp?
  • strengthen the Smallworld/Magik community. Having better educated Smallworld developers who also share their experiences with others can only help to make the Smallworld technology more attractive to new customers.
  • get real live code examples
  • meet old friends and make new connections
  • give back to the community that provides us our livelihood
  • improve your Smallworld/Magik skillset by receiving and providing mentoring
  • more interaction than a typical conference presentation

I hope to facilitate the first Smallworld Camp in the Boulder/Denver area some time between now and GITA. The only way it works is for more than 1 person to be part of it so if you are interested, please jump in and contribute to the planning at http://smallworldcamp.pbworks.com/SmallworldCampBoulder2009

I have never participated in a BarCamp or CodeCamp, but am fascinated by the concept. If you have any comments to share about your experiences in a BarCamp, please leave a comment. I would be very interested to hear if you think such a model would work in the Smallworld community.

As a final note, Smallworld Camp is not endorsed nor supported by GE. As a matter of fact, no one organization may ever plan a BarCamp. The camps are by and for developers. Corporations are encouraged to sponsor various parts of the event but all content is always driven by the participants.

Friday, October 23, 2009

Dialog Designer 1.9.5 now available

Graham Garlick at iFactor Consulting has uploaded v1.9.5 of the open source Dialog Designer. You can get the latest version by browsing to http://www.ifactorconsulting.com and clicking on the "Download Version 1.9.5 Now..." link.



Here are the release notes for 1.9.5:
- create additions/changes sub-source directories for core code modifications
- bug fix (statusbar progress indicator element XML generation/parsing)
- exposed :allow_filtering? attribute for a tabular_list
- fixed statusbar rendering of multiple springy panes
- fixed window_pane rendering

I did not do a post for 1.9 so here are those release notes as well:
- added messages tab (define messages to support user-written code)
- fix issue where module.def 'requires' section was not updating
- various bug fixes, all minor.
- added recordset_gui_component widget
- added numeric input widget
- added 6th example dialog to the Help Document

If you have not used this tool, I would highly recommend it. You can create your GUIs without using this tool, but there are so many parameters to be aware of. Using this tool essentially allows you to incorporate known (and sometimes unknown) tips, tricks, workarounds and best practices from one of the best Magik coders around and put it into your own application.

Tuesday, October 20, 2009

Smallworld High Definition (part 2)

(My apologies to those who get their blog post comments on an RSS feed. This is a repeat of a comment from last night)

At the Users Conference in Glendale this week, John Eason was very excited to take me to see the "Smallworld HD" demo. All I can say is that there was some really cool stuff. With mostly only Magik changes, the developer (a Magikian in my mind) created a new interface that out-Microsofted Microsoft. The new GUI has all the standard Microsoft UI design elements but with a few iPhone-like tricks thrown in. The graphics are clear and crisp (a la the comments from "Anonymous"). The widget classes (as well as the geometry classes) now all inherit from a common set of ancestor classes to take advantage of new canvas rendering functionality and geometry caching.

As far as I am concerned, the new GUI will be the cool kid on every user's Desktop. I expect that non-GIS users will want a copy of the GUI on their desktop just to play with it and show off its cool functionality. The feature that I think is most like the iPhone is the one where you can grab the map with your pan cursor and "flick" it in a particular direction. When you are scrolling down the object list (think combination of Smallworld Explorer result list and Google Maps search result list) you can grab an object in the list and "flick" it up or down to scroll the map. While those of us who have worked with Smallworld for a long time have know of the software's power, I think these GUI changes will make the software more attractive and inviting for first-time users to interact with. There is definitely an initial "wow" factor that is combined with functionality that gives this GUI a type of "stickiness" as well. "Good job" to the team that is working this and I wish you much success in seeing it through to delivery.

If you are at conference this week, it is worth checking out this demo at the GE booth.

(According to new US Federal Trade Commission rules, bloggers are now supposed to reveal any freebies or payments they get for reviewing a product. Unfortunately for me, no freebie was provided. I can only hope that the beta version comes out soon.)

porting sw_dotnet_acp to a TICS framework

Pedro Miranda (http://magikbites.blogspot.com) has contributed the very cool sw_dotnet_acp code to the Magik Components SourceForge community (aka mclib). Recently someone asked me if I had used this library with the TICS framework instead of with the ACP framework. I had not done that but thought that would be an interesting question to ask my readers.

If anyone is interested in collaborating on a sw_dotnet_acp port to something like sw_dotnet_tics in the context of the mclib community, please post a comment to this post or send me a note to the contact information on the sidebar of this blog.

How do interact with FME in Smallworld?

I was asked a question yesterday at the conference about how I interact with FME from Smallworld and thought I would start an informal survey of readers to see what your responses are.

The question is: How do you interact with FME from Smallworld?
1) Do you see FME as an application that is launched from a Smallworld session?
2) Or do you prefer to use FME more as a standalone tool that connects to a Smallworld server process?

My own preference is to see FME as a standalone tool that makes a connection to a Smallworld server. That way any business analyst that does not have a Smallworld session running could still make a connection to a central Smallworld server port and run the workspace. Having this configuration would also facilitate use of FME Server with Smallworld. This method of interaction would be great for reading from Smallworld. But I suppose that writing to a Smallworld database might make one lean towards option#2.

With option #2, you as a Smallworld user could control which alternative the data was being written to and then you could do a final check of the data in your alternative before you committed it.

So I suppose that my preferred mode of operation includes both above-mentioned options. A "FME connects to Smallworld server" option for reading data and for selected controlled write operations. And a "Smallworld launches FME" option for the more ad hoc write operations.

I'm curious what your thoughts are.

Monday, October 19, 2009

Version Viewer

My colleague Graham Garlick has been working on a very cool Version Viewer application. It is essentially a GUI that allows you to view/compare two different versions of a dataset. It lets you compare geometries and attributes and determine what has changed between the two versions. Imagine Smallworld's conflict detector's Affected Objects Lister but the Version Viewer is easy for you to use in non-conflict situations.

What could you use the Version Viewer for?
- recovering attributes/records from data that is available in an older view
- analysing your data BEFORE a merge/post. You could build this into a pre-emptive conflict avoidance process
- selective merge/post by attribute
- data quality checks to see the who/what/when of changes in your data. Very helpful for investigating the inevitable question: "How/why did that data get changed and in what context were the changes made".

Graham also is responsible for the Dialog Designer. He used the Dialog Designer to build the complex Version Viewer GUI. As part of the Version Viewer demo, he can show you how you can dynamically change the GUI layout of that application using Dialog Designer.\

Please come by the iFactor Consulting booth at the conference and ask for a demo by Graham. If you are not able to make it to the booth, sent me a note at the contact information on the blog sidebar and I can arrange a demo.

How to make your data positionally accurate

iFactor Consulting is getting some great interest in our Web Maps Connector here at the GE Smallworld Users Conference. But one question we have been getting is "how do we line up our data with your web maps?".

Well, Graham Garlick has an answer to that. He has a presentation at the conference on Tuesday at 9:15 that presents a tool/process that he has developed to answer this question. His presentation is called "Make your GIS data positionally accurate: Why? How?".

If you can't attend his presentation, please come by the iFactor booth to get a personal demo.

Friday, October 16, 2009

See you in Arizona!

I have been looking forward to this year's GE Smallworld Americas Users Conference which will be held in Glendale, AZ starting this Sunday. I will be at the iFactor Consulting booth. Please drop by to say hello. I look forward to meeting up with as many of you as possible.

We can talk FME, Magik, Bing Maps, Google Maps, cycling or even why 102°F weather in October in the Northern Hemisphere does not seem right to me :)


Wednesday, October 14, 2009

Getting more from tracebacks

Now that I've spammed my readers with a useless "test post via email" note, I will try to post something of value.

I have always been fond of saying "tracebacks are our friends". In my mind, tracebacks are not to be feared... the bigger the traceback the better because that means that I have more information available to me to debug the problem.

A common debugging technique is to put a call to the !traceback!() procedure in any method from which you want to determine how the call stack arrived at that method. Placing a !traceback!() call in your code won't raise a condition or stop the code flow. It will simply give you a dump of the call stack at that point.

To demonstrate how this works, I have put a !traceback!() call in the Web Map Connector's ve_tile.draw_on() method. The traceback looks as you would expect...

---- traceback: renderer (heavy_thread 3611775) ----
time=14/10/2009 17:00:21
sw!version=4.1.0 (swaf)
os_text_encoding=cp1252
!snapshot_traceback?!=False

!traceback!()
a ve_tile(detached) .draw_on(a sw:canvas)
sw:simple_vector:[1-11].fast_keys_and_elements()
sw:keyed_set(1).valid_indices(False)
sw:keyed_set(1).fast_elements()
sw:keyed_set(1).elements()
transient_rwo_collection(road_tile).fast_elements()
sw:simple_vector:[1-11].fast_keys_and_elements()
sw:hash_table(1).valid_indices(False)
sw:hash_table(1).fast_elements()
ve_dataset(ve_dataset).scan_elements(a bounding_box, sw:rope:[1-1], sw:hash_table(0))
a sw:uds_geometry_set(ve_dataset,0,0).scan_elements(a bounding_box, sw:rope:[1-1], sw:hash_table(0))
a sw:uds_geometry_set(ve_dataset,0,0).elements()
a sw:uds_geometry_set(ve_dataset,0,0).draw_on(a sw:canvas, unset)
sw:rope:[1-4].elements()
a sw:composite_geometry_set.draw_on(a sw:canvas, unset)
a sw:canvas.paint(True, a sw:composite_geometry_set, unset)
a sw:canvas.draw(a sw:composite_geometry_set)
map_view(map(Electricity),region).super(map_view).original!int!do_render(unset)
map_view(map(Electricity),region).int!do_render(unset)
renderer(map_view(map(Electricity),region), unset)
light_thread_launcher_proc_990928()


There is a global variable !snapshot_traceback?! that you set to _true to give you a bit more information.

MagikSF> !snapshot_traceback?! << _true
$


Now the traceback looks like...

---- traceback: renderer (heavy_thread 3563153) ----
time=14/10/2009 17:01:44
sw!version=4.1.0 (swaf)
os_text_encoding=cp1252
!snapshot_traceback?!=True

[1] !traceback!()
[2] a ve_tile(detached) .draw_on(a sw:canvas)
[3] sw:simple_vector:[1-11].fast_keys_and_elements()
[4] sw:keyed_set(1).valid_indices(False)
[5] sw:keyed_set(1).fast_elements()
[6] sw:keyed_set(1).elements()
[7] transient_rwo_collection(road_tile).fast_elements()
[8] sw:simple_vector:[1-11].fast_keys_and_elements()
[9] sw:hash_table(1).valid_indices(False)
[10] sw:hash_table(1).fast_elements()
[11] ve_dataset(ve_dataset).scan_elements(a bounding_box, sw:rope:[1-1], sw:hash_table(0))
[12] a sw:uds_geometry_set(ve_dataset,0,0).scan_elements(a bounding_box, sw:rope:[1-1], sw:hash_table(0))
[13] a sw:uds_geometry_set(ve_dataset,0,0).elements()
[14] a sw:uds_geometry_set(ve_dataset,0,0).draw_on(a sw:canvas, unset)
[15] sw:rope:[1-4].elements()
[16] a sw:composite_geometry_set.draw_on(a sw:canvas, unset)
[17] a sw:canvas.paint(True, a sw:composite_geometry_set, unset)
[18] a sw:canvas.draw(a sw:composite_geometry_set)
[19] map_view(map(Electricity),region).super(map_view).original!int!do_render(unset)
[20] map_view(map(Electricity),region).int!do_render(unset)
[21] renderer(map_view(map(Electricity),region), unset)
[22] light_thread_launcher_proc_990928()



... as you can see, each traceback line is numbered. This is kind of handy to have. The real benefits of !snapshot_traceback?! are described in the public comments for this global variable.

What I wanted to show you is a modification I made to the !traceback!() procedure that gives you even more helpful information whilst !snapshot_traceback?! is _true.

Here is my hack of the !traceback!() procedure.

#% text_encoding = iso8859_1
_package sw
$
_global !traceback! <<
_proc@traceback(_optional stream, max_depth, full?)
## Display traceback of calls in the current thread on
## STREAM. If max_depth is given, at most that number of
## stack frames will be displayed.
##
## If !traceback_show_args?! is true, then the arguments will
## be shown along with the method name.
##
## Tracebacks normally exclude stack frames after a
## condition.raise(). Setting FULL? to _true will force the
## full traceback to be shown. I.e including tracebacks of any
## condition handlers.

_thisthread.traceback(stream, !traceback_show_args?!,max_depth, full?)

_global !snapshot_traceback?!

_if !snapshot_traceback?! _is _true
_then

_global !ts!
_local sfile , meth

write("These numbered source files correspond to the methods in the numbered lines in the traceback above.")

_for i,v _over !ts!.fast_keys_and_elements()
_loop
meth << v.receiver.method(v.message)

_if meth _is _unset
_then
show(write_string("[",i,"]"))
_continue
_endif

sfile << meth.source_file
_dynamic !show_string_length! << sfile.size
show(write_string("[",i,"]"),sfile)
_endloop
_endif
_endproc
$



You can see that it calls the standard _thisthread.traceback() method, but after that it takes a list of all the methods/procedures that were reported in the traceback and shows their source files as well...


---- traceback: renderer (heavy_thread 3514507) ----
time=14/10/2009 17:05:41
sw!version=4.1.0 (swaf)
os_text_encoding=cp1252
!snapshot_traceback?!=True

[1] traceback()
[2] a ve_tile(detached) .draw_on(a sw:canvas)
[3] sw:simple_vector:[1-11].fast_keys_and_elements()
[4] sw:keyed_set(1).valid_indices(False)
[5] sw:keyed_set(1).fast_elements()
[6] sw:keyed_set(1).elements()
[7] transient_rwo_collection(road_tile).fast_elements()
[8] sw:simple_vector:[1-11].fast_keys_and_elements()
[9] sw:hash_table(1).valid_indices(False)
[10] sw:hash_table(1).fast_elements()
[11] ve_dataset(ve_dataset).scan_elements(a bounding_box, sw:rope:[1-1], sw:hash_table(0))
[12] a sw:uds_geometry_set(ve_dataset,0,0).scan_elements(a bounding_box, sw:rope:[1-1], sw:hash_table(0))
[13] a sw:uds_geometry_set(ve_dataset,0,0).elements()
[14] a sw:uds_geometry_set(ve_dataset,0,0).draw_on(a sw:canvas, unset)
[15] sw:rope:[1-4].elements()
[16] a sw:composite_geometry_set.draw_on(a sw:canvas, unset)
[17] a sw:canvas.paint(True, a sw:composite_geometry_set, unset)
[18] a sw:canvas.draw(a sw:composite_geometry_set)
[19] map_view(map(Electricity),region).super(map_view).original!int!do_render(unset)
[20] map_view(map(Electricity),region).int!do_render(unset)
[21] renderer(map_view(map(Electricity),region), unset)
[22] light_thread_launcher_proc_990928()
These numbered source files correspond to the methods in the numbered lines in the traceback above.
"[1]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/general/procedure.magik"
"[2]" "C:\DOCUME~1\Alfred\LOCALS~1\Temp\TAlfred9412q0"
"[3]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/mixins/ro_indexed_collection_mixin.magik"
"[4]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/new_sys_changes.magik"
"[5]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/convert2_sf/hashing/hash_values_helper.magik"
"[6]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/new_sys_changes.magik"
"[7]" "c:/swdev/build/410/410_PB_WINDOWS/unshipped/source/sys_core/ds_src/dd/dd_collection_mixin.magik"
"[8]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/mixins/ro_indexed_collection_mixin.magik"
"[9]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/new_sys_changes.magik"
"[10]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/convert2_sf/hashing/hash_values_helper.magik"
"[11]" "C:/ifactor/products/workspace/Virtual_Earth_Connector/trunk/modules/ve_som/source\ve_dataset.magik"
"[12]" "C:/Smallworld41/product/modules/sw_core/geometry_vector/source/uds_geometry_set.magik"
"[13]" "C:/Smallworld41/product/modules/sw_core/geometry_vector/source/uds_geometry_set.magik"
"[14]" "C:/Smallworld41/product/modules/sw_core/geometry_vector/source/geometry_set_mixin.magik"
"[15]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/mixins/rope_mixin.magik"
"[16]" "C:/Smallworld41/product/modules/sw_core/geometry_vector/source/composite_geometry_set.magik"
"[17]" "c:/swdev/build/410/410_PB_WINDOWS/unshipped/source/sys_core/mgr_src/user/drawing_surface_mixin.magik"
"[18]" "c:/swdev/build/410/410_PB_WINDOWS/unshipped/source/sys_core/mgr_src/user/drawing_surface_mixin.magik"
"[19]" "C:\ifactor\products\workspace\Virtual_Earth_Connector\trunk\source\patches_z_proposed\p1000001_1_if_vec_map_plugin.magik"
"[20]" "C:\ifactor\products\workspace\Virtual_Earth_Connector\trunk\source\patches_z_proposed\p1000001_1_if_vec_map_plugin.magik"
"[21]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/general/procedure.magik"
"[22]" "/swdev/streams/leo/release/unshipped/source/sys_core/sys_src/general/procedure.magik"


This can be very helpful when you are trying to debug why certain code works in one of your environments but not the other. It can be very helpful to the GE Help Desk team because sending this enhanced traceback will tell them how your call stack is different than the one they are testing in. It will show which methods were changed in patches and in custom code.
Test post via email.

Monday, October 5, 2009

Preventing ACE posting

It has long been known that posting up ACE and/or CASE alternatives may have unintended consequences when the parent/child alternatives have records that are in conflict. If you ever want to prevent all users from posting an ACE alternative, you can use this code I wrote to do that.

This code snippet has been written in a patch format. You can read more about how to create your own patches here.
The define_method_synonym() approach is described here. Note that because the core method database_view.post() source code is not shipped with the product, we need to use the define_method_synonym() approach to wrap our own custom functionality around it without modifying the core restricted functionality.


#% text_encoding = iso8859_1
_package sw
$
sw!patch_software( "ds_src", 1 )
$
# use define_method_synonym() to wrap some of our behavior
# around the core method without redefining the core behavior.
_if database_view.method(:original!post|()|) _is _unset
_then
database_view.define_method_synonym(:original!post|()|,:post|()|)
_endif
$
_pragma(classify_level=restricted, usage={redefinable})
_method database_view.post(_gather args)
## Post changes in this alternative to the parent,
## performing an implicit commit() if any updates are
## outstanding.
##
## Returns _true if there were changes to post, _false
## otherwise.
##
## If the data dictionary has been modified, then the modified
## version must have been allocated a datamodel version first.
##
## External databases are checked to ensure that if they are
## updated, that they are prepared to commit (i.e. they are
## running). An error is raised if one is not prepared to
## commit.
##
## THIS REDEFINED VERSION OF THE METHOD HAS BEEN MODIFIED TO
## DISALLOW POSTING ACE ALTERNATIVES.

_if _self.name.canonical _is :ace
_then
condition.raise(:cannot_post_ace_database)
_endif
_return _self.original!post(_scatter args)
_endmethod
$
_pragma(classify_level=restricted, usage={redefinable})
## raised by database_view.post() when database_view is named
## :ace.
condition.define_condition(:cannot_post_ace_database,:user_error,{})
$
read_message_patch(:condition,_unset,:en_us)
$
:cannot_post_ace_database Posting ACE alternatives is not safe and has been disabled.
$

sw!declare_patch(1000000001,"1cst","Prevents posting ACE alternative.")
$