Showing posts with label Software Development. Show all posts
Showing posts with label Software Development. Show all posts

Friday, December 8, 2023

Automating REST Authorization Calls

REST services provided by 3PL partners generally require a two-step process to use their service.  First a call must be made with secret credentials to obtain an authorization token.  Then the auth token is submitted with subsequent requests.

In Manage 2000 REST.SERVICES we can create one REST.SERVICES record to obtain the auth token and then reference it in another REST.SERVICES record:



GetAuthToken accepts JPATH expression parameters to identify the token and the expiration span. It then caches the token for the expiration span and uses the cached version until it expires before going back to the REST service to get a fresh token.

Services like the FEDEX Rates service accept a great many parameters.  These can be specified in the REST.SERVICES record:

A SCREEN.BUILD screen built with matching X_DATA_1 names makes it easy to map the inputs into the RS.COM area for calling the 3PL REST API for information.


The program needs to:

  • Call SUB.MT500 to run the screen
  • Overlay the variables from the screen to the REST common
  • Call REST.SERVICE
  • Parse the result with JPATH
  • Present the results to user, perhaps in the PWS Viewer using SUB.TEXT.OUTPUT.
And the result is a Manage 2000 look and feel function that gets live information from the 3PL REST service:




Thursday, November 7, 2013

Selecting From PC Most Recently Used Files

Taking the last two posts to a final conclusion, can we show the user the most recent PC files they have been working with in most recent order instead of showing their My Documents files in alphabetical order?

The answer is yes; using another CSIDL constant that represents the Recent Files directory, CSIDL_RECENT.

 * 
250* Pick a MRU file 
*
ANSWER = ''
GOSUB SelectFromMRU ;* Cross Reference using PCDirectoryInformation MRU
IF ANSWER # '' THEN
MSO.USER.VARS = '"':DirectoryPath:'\':ANSWER:'"'
CALL SUB.EXECUTE.MSO('2324C', '', '', '')
GO 250; *Let user choose another until none selected
END
*
RETURN
*
SelectFromMRU:* Cross Reference from PC File System MRU
*
DirectoryPath = SHGetFolderPath(CSIDL_RECENT)
Mask = '*.*'
ANSWER = ''
*
* Ask PC for Directory listing
*
HiddenFiles = False;SystemFiles = False;DoCheckSum = False;Recursive = False     
DIR.TEXT = PCDirectoryInformation(DirectoryPath,Mask,HiddenFiles,SystemFiles,DoCheckSum,Recursive)
*
* Format directory listing as OPTION list for OPEN.LIST.BOX
*
OPTION.TEXT = '';OPTION.RETURN.VALUES = ''; OPTION.SORT.KEYS = ''
DIR.ENTRIES.CNT = DCOUNT(DIR.TEXT<1>,@VM)
FOR DIR.IDX = 1 TO DIR.ENTRIES.CNT
DIR.ENTRY = DIR.TEXT<1>
DIR.ENTRY.END = DIR.ENTRY[1]
IF DIR.ENTRY.END # ":" AND DIR.ENTRY.END # "\" AND DIR.ENTRY.END # "/" THEN 
FILE.NAME = GetPCFileName(DIR.ENTRY)
IF FILE.NAME[1,1] # '~' THEN ;* Ignore Temp Files from currently open Office docs
DISPLAY.FILE.NAME = FILE.NAME[1, INDEX(FILE.NAME, ".lnk", 1)-1]
SWAP "~" WITH "_" IN DISPLAY.FILE.NAME
FILE.DATE.STAMP = DIR.TEXT<3>
FILE.TIME.STAMP = DIR.TEXT<4>
IF NOT(NUM(FILE.DATE.STAMP)) THEN FILE.DATE.STAMP = 0
IF NOT(NUM(FILE.TIME.STAMP)) THEN FILE.TIME.STAMP = 0
SORT.KEY = FILE.DATE.STAMP * 86400 + FILE.TIME.STAMP
LOCATE SORT.KEY IN OPTION.SORT.KEYS<1> BY "DR" SETTING POS ELSE
END
INS SORT.KEY BEFORE OPTION.SORT.KEYS
FILE.DATE.STAMP = OCONV(FILE.DATE.STAMP,"D2-")
FILE.TIME.STAMP = OCONV(FILE.TIME.STAMP, "MT")
BYTE.CNT = OCONV(DIR.TEXT<5>, "MD0,")
INS DISPLAY.FILE.NAME:"~":FILE.DATE.STAMP "L#8":"~":FILE.TIME.STAMP "L#8":"~":BYTE.CNT "L#15" BEFORE OPTION.TEXT<pos>
INS FILE.NAME BEFORE OPTION.RETURN.VALUES<pos>
END
END
NEXT
IF OPTION.RETURN.VALUES = '' THEN RETURN;* No Files Found
*  
* Setup call to OPEN.LIST.BOX
*
PROMPT.FOR.ANSWER = 1;DELETE.ALLOWED = 0;MULTI.SELECT = 0;FORCE.SELECT = 1;AUTOSELECT = 1;INFO.BUTTON = 0
INFO.SUB.NAME = '';INFO.SUB.KEYS = '';ADD.TEXT = '';BOTTOM.TITLE = ""
TOP.TITLE = DirectoryPath:" File Listing":@AM:"File Name~Date~Time~Size":@AM:"~Modified~Modified~"
NBR.COLS = DCOUNT(OPTION.TEXT<1>,'~')
CALL OPEN.LIST.BOX(OPTION.TEXT, OPTION.RETURN.VALUES, MULTI.SELECT, FORCE.SELECT, AUTOSELECT,  INFO.BUTTON, TOP.TITLE, BOTTOM.TITLE,  NBR.COLS, PROMPT.FOR.ANSWER, ANSWER, DELETE.ALLOWED, INFO.SUB.NAME, INFO.SUB.KEYS, ADD.TEXT)
IF ANSWER # '' THEN 
* Remove ok/delete flag from answer 
ANSWER = ANSWER<1> 
END
*
RETURN

You can find all the available CSIDL constants defined for SHGetFolderPath in the COPY.TOOLS.BP item PWS.CODES, which is where functions such as SHGetFolderPath and PCDirectoryInformation are declared.

To make them available in your code just include the copy item after the STANDARD.COMMON.VARIABLES declaration at the top of your code:

* Release Information
* MANAGE-2000 - PMN - Release 8.0
* PMN.TEST - TESTING MSO SUBROUTINE API
* Written                                                pmn 11-01-13
*#* COPY COPY.TOOLS.BP STANDARD.VARIABLES.1 (REPLACING PGM.NAME BY PMN.EXECUTE.MSO, FN.NAME BY PMN.TEST, IO.OPEN.OPTS BY TERM.DATA) ;*#* Copied Source Follows (11-01-13)
$INCLUDE STANDARD.COMMON.VARIABLES FROM COPY.TOOLS.BP
$INCLUDE STANDARD.COMMON.APP.PROGRAMS FROM COPY.TOOLS.BP
$INCLUDE STANDARD.VARIABLES.END FROM COPY.TOOLS.BP
PGM.NAME='PMN.EXECUTE.MSO'; FN.NAME ='PMN.TEST'
CALL IO.OPEN('TERM.DATA',PASSWORDS)
*#*
*
* Other Variables
*
$INCLUDE TrueFalse FROM COPY.TOOLS.BP
$INCLUDE PWS.CODES FROM COPY.TOOLS.BP

Wednesday, November 6, 2013

Cross Referencing PC File Directory

In the last post I casually suggested putting the selected filename in the Last Keys store for the IO.SCRATCH file.  This begs the question "How do I let the user select a file from their PC while in PWS?"

The answer to this question is the PCDirectoryInformation function which can access a directory listing and OPEN.LIST.BOX which can present the user with a CROSS.REF-like listbox.

*
200* Pick a file from a pc directory and run it
*
DirectoryPath = SHGetFolderPath(CSIDL_PERSONAL)
Mask = '*.*'
ANSWER = '' 
GOSUB CrossRefPCDir ;* Cross Reference using PCDirectoryInformation
IF ANSWER # '' THEN
MSO.USER.VARS = '"':DirectoryPath:'\':ANSWER:'"'
CALL SUB.EXECUTE.MSO('2324C', '', '', '')
GO 200; *Let user choose another until none selected
END
*
RETURN
*
CrossRefPCDir:* Cross Reference from PC File System
*
* Ask PC for Directory listing
*
HiddenFiles = False;SystemFiles = False;DoCheckSum = False;Recursive = False
DIR.TEXT = PCDirectoryInformation(DirectoryPath,Mask,HiddenFiles,SystemFiles,DoCheckSum,Recursive)
*
* Format directory listing as OPTION list for OPEN.LIST.BOX
*
OPTION.IDX = 0;OPTION.TEXT = '';OPTION.RETURN.VALUES = ''
DIR.ENTRIES.CNT = DCOUNT(DIR.TEXT<1>,@VM)
FOR DIR.IDX = 1 TO DIR.ENTRIES.CNT
DIR.ENTRY = DIR.TEXT<1,DIR.IDX>
DIR.ENTRY.END = DIR.ENTRY[1]
IF DIR.ENTRY.END # ":" AND DIR.ENTRY.END # "\" AND DIR.ENTRY.END # "/" THEN 
FILE.NAME = GetPCFileName(DIR.ENTRY)
IF FILE.NAME[1,1] # '~' THEN ;* Ignore Temp Files from currently open Office docs
OPTION.IDX += 1
FILE.DATE.STAMP = OCONV(DIR.TEXT<3,DIR.IDX>,"D2-")
FILE.TIME.STAMP = OCONV(DIR.TEXT<4,DIR.IDX>, "MT")
BYTE.CNT = OCONV(DIR.TEXT<5,DIR.IDX>, "MD0,")
OPTION.TEXT<OPTION.IDX> = FILE.NAME:"~":FILE.DATE.STAMP "L#8":"~":FILE.TIME.STAMP "L#8":"~":BYTE.CNT "L#15"
OPTION.RETURN.VALUES<OPTION.IDX> = FILE.NAME
END
END
NEXT
IF OPTION.IDX = 0 THEN RETURN;* No Files Found
*  
* Setup call to OPEN.LIST.BOX
*
PROMPT.FOR.ANSWER = 1;DELETE.ALLOWED = 0;MULTI.SELECT = 0;FORCE.SELECT = 1;AUTOSELECT = 1;INFO.BUTTON = 0INFO.SUB.NAME = '';INFO.SUB.KEYS = '';ADD.TEXT = '';BOTTOM.TITLE = ""
TOP.TITLE = DirectoryPath:" File Listing":@AM:"File Name~Date~Time~Size":@AM:"~Modified~Modified~"
NBR.COLS = DCOUNT(OPTION.TEXT<1>,'~')
CALL OPEN.LIST.BOX(OPTION.TEXT, OPTION.RETURN.VALUES, MULTI.SELECT, FORCE.SELECT, AUTOSELECT,  INFO.BUTTON, TOP.TITLE, BOTTOM.TITLE,  NBR.COLS, PROMPT.FOR.ANSWER, ANSWER, DELETE.ALLOWED, INFO.SUB.NAME, INFO.SUB.KEYS, ADD.TEXT)
IF ANSWER # '' THEN 
* Remove ok/delete flag from answer
ANSWER = ANSWER<1,1> 
END
*
RETURN



Monday, November 4, 2013

Programmatically Launching Related Documents

Manage 2000 includes support for attaching documents and other objects to records in the ERP database and having them available for users to render.  This is normally implemented through links defined in the OBJECT.IDX file and that is a good way to go, whether you attach objects manually or generate entries in OBJECTS and OBJECT.IDX files programmatically.

But sometimes you may want a more dynamic mechanism that lets the user drill from Manage 2000 screens into external documents without building a permanent link in OBJECTS.IDX. Here is one approach:

PC.EXECUTE.MSO is an MSO subroutine that requests PWS to perform a DDE.START.  It is meant to be wrapped by an MSO like 15S which launches Notepad.

You can copy 15S in MSO.BUILD to make a custom MSO that will launch whatever program or document you want on the user's PC.



To make an MSO for launching PDFs set PC_Execute_Name to a PDF viewer execute-able. (This depends on all PC's using this MSO having the same PDF viewer installed at the same location.)

To run any MSO out of basic code you can call

CALL SUB.EXECUTE.MSO(OBJECT.LIST, CAPTION, CLOSE.OPTIONS, PROPERTIES)

So in your program (perhaps a PWS button subroutine) concatenate together the file path for the currently selected external document and tuck it into the last keys for IO.SCRATCH (just using this as work space to pass the variable into the MSO).

DMY = SetLastKey("IO.SCRATCH", "C:\Temp\redpages_net.pdf");*  (this is 7.3 code for update last keys)
CALL SUB.EXECUTE.MSO('2324C', '', '', '')

If you find hijacking the IO.SCRATCH last key distasteful there is a more legitemate mechanism for passing program variables into MSOs:

In your program you can store attributes in the MSO.USER.VARS variable (delcared in  STANDARD.COMMON.VARIABLES)

MSO.USER.VARS = "C:\Temp\redpages_net.pdf"
CALL SUB.EXECUTE.MSO('2324C', '', '', '')

 and then in your MSO properties refer to them using the ampersand reference &USR<1>&, &USR<2>&...


If you want a more general approach you can use LAUNCH.URL.MSO in place of PC.EXECUTE.MSO.  LAUNCH.URL.MSO starts a document rather than a program and allows Windows to decide which application should be used to open that document.  The "Address" property of LAUNCH.URL.MSO can be a URL or it can be a disk location preceded by "FILE://" in which case Windows will lookup an application from the file extension.


    MSO.USER.VARS = "C:\TEMP\redpages_net.pdf"
    CALL SUB.EXECUTE.MSO('2324C', '', '', '')



Tuesday, March 12, 2013

Manage 2000 Control Extenders Explained

M2kDataBinderControlExtender:

Originally this was an alternative to placing single valued fields in FormView containers.  The VS2005-2008 MS databinding changes mandated that a table be bound to a FormView and that textboxes and such in that FormView could then be bound to a field in that table. There are a bunch of things I find objectionable here.

Anyway, I stole the code for this extender from Rick Strahl at WestWind (Hawaii, poor chap), a great source for .Net intelligent thought http://www.west-wind.com/weblog/.  The databinder moves fields into and out of datasets from web controls.

Visual Studio 2003 only did one way binding using the ‘eval’ method, and we had to write code to move data back. VS 2005/8 added the ‘bind’ method to move data from web controls back into datasets.

I prefered the method name ‘Fill’ to describe moving fields into the dataset.  The name was always used in DataAdapters to mean moving the fields into the dataset from the datasource.  So I chose to use the DBCE.Fill expression to request the databinder to fill up the dataset with field values from web controls.

While working on the m2kScrolledSetControlExtender I enhanced this extender to bind fields from dataset to columns in a JSON array stored in a hidden field (Bind To Client).    The scrolledset then moves fields between a hidden field and textboxes inside the browser using javascript. The hiddenfield acts like FILE.ITEM(1) providing working storage at the browser end.   Together these extenders manage the transmission from datasets to scrolledsets and back.

Since then, based on application work by the development team, I have been adding additional support in the data binder for other situations like an easy way to bind SA MV sets to multi-line textboxes and just lately support for RadioButtonLists.

The Microsoft databinding patterns can still be used and, in fact, for grid displays and other sorts of table level binding I expect we will continue to use ObjectDataSource controls.

M2kScrolledSetControlExtender:

My original goal for the scrolled set extender was to create a way to have scrolled set processing in web pages that matched the keyboarding and development efficiencies of SUB.MT500 and SCROLL.MAINT. Yeah, not quite there yet :)

In one sense the scrolled set extender stands in for SCREEN.BUILD C4.  It provides a design time place for the developer to specify scrolled set behavior.  It also serves to emit the required javascript for instantiating a javascript object with the operational mechanics implementing the scrolled set experience.  The definition for the javascript object and all of its methods actual exists in roiGlobal.js.  The scrolledset extender just needs to create an object of that class in the browser world and initialize it with parameters supplied by the application developer.

One of the principal design decisions was to leverage all the data entry power of the roiTextBox, and to loosely package an arbitrary number of entry fields in a table that would contain the set entry controls and display cells. I wrote up a how to with more details on creating scrolled sets named WEB.SCROLLED.SET and added it to the SYS.WEB.HOWTOS menu.

M2kMetaDataControlExtender:

This extender is basically a re-write of the EditParams features that we originally built into roiTextBox and the metadata data access component. It provides PMT like definitions that developers can use to describe the behavior they want and then the Tools layer can take care of the implementation.


When Microsoft broke the component design surface into a separate tab in Visual Studio 2005/2008 they COMPLETELY isolated WebControl design time from Data Access Component design time.  This meant that the roiTextBox had no way of talking to the typed datasets or the metadata component.  So the whole metadata world and the databinding got shoe-horned into “WebControls” (which the control extenders are) so they can sit in the same designer and interact with webcontrols.

Thursday, October 11, 2012

How to develop web functions with Visual Studio

There are a number of different sub-topics here:

  1. Creating a developer sandbox on your desktop
  2. Loading the Manage 2000 DAC components and WebControls into Visual Studio
  3. Configuring Visual Studio to 'see' the Manage 2000 web function wizards
  4. Where to find out more about Manage 2000 Web Technology and how to use it
You can find answers on all of these topics in Manage 2000 7.3 sp3 on the SYS.WEB.HOWTOS menu:
  1. SYS.DEV.SETUP describes how to set up your PC for Manage 2000 web function development.
  2. WEB.VS.TOOLBOX.SETUP describes how to add Manage 2000 Web Components and Controls into your installation of Visual Studio.
  3. WEB.VS.WIZARDS.SETUP describes how to configure Visual Studio to 'see' the M2k wizards.
And in the related courseware section you will find links to INTRO.WEB.COURSE and ADV.WEB.COURSE which are chock-full of web function development minutia.

But if you want to get somewhere quickly check out the new wizards for generating Visual Studio projects for HyperQueries, Mobile Web, and BTO Posting type functions.  These wizards allow you to describe your function in terms of Unidata file names and dictionaries, or in terms of Manage 2000 Business Transactions and then generate for you a working prototype with controls configured and named in useful ways and in some cases with extensive amounts of code-behind already plumbed out.

There are a couple of webex videos available under the Manage 2000 Web Presentations link above on the right.  For Visual Studio Manage 2000 web function development knowledge check out Mastering ASP.NET 3.5 and VisualStudio2008 for Manage2000 Parts I and II.

Saturday, January 7, 2012

In Defense of the Multi-Value Database

Reading Andrew E. Wade's chapter "Business Objects in Object Databases" of Andrew Carmichael's anthology of OO practitioners "Developing Business Objects" from 1997 I am struck by the parallels between the advantages of Object Oriented databases and Multi-Value databases over RDBMS for complex business modeling such as ERP systems.
RDBMSs provide a simple, flat, tabular view of all information. The user must map his application data structures, whatever they might be, into those tables. Other traditional database systems require similar mapping down to simple, flat records. The user must work at the level of only the table data structure and only a small set of simple operations (select,project,join). Now, if the application data structures is naturally simple and flat, and the application operations are also simple, this mapping is straightforward and not an issue. On the other hand, when the application structures and operations become complex, the RDMBS approach forces the user to deal with this directly. An ODBMS offers the ability to model an application in terms of objects, which may have any user-defined structure and any user-defined operations. Applications that require nested structures, dynamically varying sized structures, many-to-many relationships, and complex operations can map these directly to objects. Not only is the support far more efficient, far faster at run time, but the user may work at higher levels of abstraction. Instead of translating the application down to records and tuples with joins, the user can work directly with objects such as customers or manufacturing processes, and the natural, application-defined operations on these. The result is easier and faster for the developer and the end user. ...

When to Use an ODMS

... Second, if the application's information is complex and interconnected, an ODBMS can provide much better performance and ease of use. To contrast, if the information being modeled consists of simple, flat, fixed-length field, that fit nicely into tables, an RDMBS can be a fine fit. On the other hand, if the information contains complex structure, nested structures, dynamically varying sized structures, including images, audio, and video, all these can be represented directly as objects. Saving the need to translate not only eases the developer's load, but also eliminates the need to translate at run time, making it faster. To some users, the interconnections in their information model are even more important. In an RDBMS, such relationships are represented by creating secondary data structures, foreign keys. At run time, the system searches down two tables, comparing key values, until it discovers two that match.This search-and -compare process is called a join, an it's the weak point of relational technology. Even with the help of indices, it's slow, and the bigger the database, the slower it gets. In an ODBMS, there is no need to create and manage secondary data structures... Referential integrity, a difficult issue in traditional database systems, comes along automatically and easily. Moreover, the traversal is direct, with no need to search or compare, resulting in performance that is orders of magnitude faster. The more relationships, the more the application will benefit from an ODMBS.
Although to my way of thinking the most critical advantage of Multi-Value databases is their elasticity. They accomodate change. And change is the greatest challenge to software development and software field operations. One example of this is the ability to add domain specific vocabulary to a database in the field. The I-Descriptor feature in file dictionaries allows one (among other things) to describe how the system should retrieve a single foreign field. This is, effectively, a class definition for a field level join. Once defined the foreign sourced field may be used as if it were local for query and record selection purposes. Some users shy away from creating new custom dictionaries, but I see this as teaching the database how to be more useful to the users, how to be more functional in the users natural business vocabulary. Some argue that Multi-Value is dead. I hear this mostly in context of sales and marketing. But the absolute dominance of the healthcare sector by Intersystem's Cache and its predecessor MUMPS would seem to contradict this assertion. Critics will point out that Cache is not Multi-Value, but it most certainly is NOT first normal! In fact, one can argue that the Multi-Value database design is a subset of Cache's architecture. I would argue that the default Multi-Value Telnet based UI is the major problem for sales and marketing. Cache supports HTML as an immediate client protocol. This has been a conspicuously missing piece in U2 and other Multi-Value vendors offerings. While there are many ways to mix-in HTML client protocols such as UO, RBO, BlueFinity, or developing your own data components as we did on the Manage 2000 team, these all introduce tons of extra complexity. On most Multi-Value platforms there is no native MV Basic way to simply and directly code HTML UI's.

Sunday, January 30, 2011

iManage2000

This last month I've been testing and researching issues related to accessing Manage 2000 web functions from IPhone and Android based mobile devices. Since these internet enabled devices include web browsers the basic ability to run Manage 2000 web functions is built right in.
However there are stumbling blocks to their usability. The most obvious problem is the small screen and the large complex pages of standard Manage 2000 web function pages. A little further examination reveals navigation problems in the drop down menus. These are rendered by Infragistics UltraWebMenu controls which do not work properly with WebKit based browsers. Another issue to consider is performance when accessing the net through slower connections. (How many G's are there in T1?)
So, though you can go the discount route and setup phone desktop buttons to target specific standard Manage 2000 web functions, a little more work can lead to a much better user experience.
The road map to a better user experience includes replacing the standard heading banner with a navigation app based on IUI, WebApp.Net, or Sencha Touch. The menus should still be administerable in MENU.BUILD, but the whole mobile menu tree would get loaded to the mobile browser as a multi-layer page with anchor navigation between layers. This would provide the user with rapid menu navigation including familiar slide transitions and header navigation buttons.
Functions would work off of standard hyperlinks and load and postback in their usual fashion. Though any specific mobile web function development should consider leveraging the layer tools in these micro-frameworks. Other mobile web function best practices would include avoiding the IG Grid in lieu of lighter weight options, minimizing the information displayed in any given layer to 5-7 items and providing multiple choice data entry in place of fill in the blank wherever possible.
Sizing the formatting table to 480 x 320 can help give a design time feel for mobile screen territory at standard magnification. HTML meta tags like <meta name="viewport" content="width=device-width; initial-scale=1.0;" /> can be used to control the magnification level of the web function when initially displayed. Since these micro frameworks tend to use CSS hiding techniques they interfere with the design time WSYIWYG in Visual Studio. You can temporarily comment out the stylesheet link in the aspx file to get a better designer experience.

Friday, December 17, 2010

2 bits 4 bits 64 bits a dollar

The good news: "Mr Newby your new 64 bit Windows-7 8 gig package is ready for you."
The bad news: "Program Files or Program Files (x86)" and "How about that WOW6432Node Eh!"
Sooooooo, I've been installing and uninstalling and tweaking vbproj files and futsing with Visual Build scripts and messing about with InstallShield 2010 scripts and repeating, ad naseum.
But if you are a developer loading up Manage 2000 7.3 sp2 web tools on 64 bit Windows 7 box and you should run into issues, be thankful, it could be much much worse. The sp2 installs are now much more solid.
The most likely annoyance will be dataset references pointing into "Program Files" that can't be resolved because the dataset libraries are now in "Program Files (x86)". You can, of course, simply delete them and then re-add them from the correct location on your box.
Or you can copy the typed library en-masse from Program Files (x86) to Program Files. You just need to remember to copy over dlls anytime you regenerate them through the dataset utility.
Another option is to use the Path Conversion Utility to do a mass change on all the vbproj files in a Manage 2000 web site. This makes sense if all of your production and development web servers are 64bit.
I have also added a PreBuild.cmd script at the Manage2000.vbproj level that you can add under advanced compile options "..\..\..\PreBuild.cmd" for a more surgical approach. As you build projects it will convert the references from hardcoded to "$(ProgramFiles)" which will load and build in either 64 or 32 bit environment. This will prompt you to reload the project during the build if it finds references that must be changed. Otherwise it will let the build continue normally.

Saturday, November 6, 2010

Manage 2000 7.4 Web - The Beginnings

Manage 2000 7.3 field release is well underway and I have finally got some of the bug reports I have been begging for since Beta. Yes, be careful what you wish for. But each one is a treasure that will make 7.3 sp2 a better more solid product when it launches in Q1 of 2011.

Perspectives 2010 is done and now I can turn my attentions to some serious development work for Manage 2000 7.4. My first major task is to build a foundation for web update functions that will be as nice for the keyboard oriented users as PWS functions and that will be as quick to build for the developer.

There have been 4 or 5 update web functions in Manage 2000 since release 7.1 and the BTO framework has always supported update business object processes. But is is not an easy task to build an update web function and the user experience falters particularly for keyboard oriented folks in scrolled set scenarios.

It is remarkable how effective one level of de-normalization in the user interface can be. When you look around at the thousands of Manage 2000 PWS functions almost all of the screens are a collection of single valued prompts with a few scrolled sets. The application developer can express the modeling of complex business processes quite efficiently with this one level de-normalization, taking the user through multiple screens/pages traversing deeper business processing levels. And I wonder if this does not reflect a characteristic of human perception to be interested in a set of data and its immediate context, but no further.

Using grids to implement scrolled sets is like converting an all-terrain vehicle into a riding lawn mower; sure it can be done, but is it the most practical approach? The programming model is too complex and the performance too poor. A much leaner model that only does what is required for scrolled set entry and does not try to be a generalized do everything-2-dimensional container is needed.

So here I am re-casting the SCROLL.MAINT logic Doug wrote 20 years ago into an HTML/JavaScript mold. Am I going backwards, or am I bringing those things that worked and added value into the Dot World?

Tuesday, September 21, 2010

REST Web Services from UniBasic

In the midst of working up some labs for Perspectives I started thinking about REST web service access from UniBasic. The lab I happened to be working-up is on SOAP based RPC web service usage, for which we have a number of working examples and implemented projects.

But in creating services for Manage 2000 internal consumption I have all but abandoned SOAP RPC in favor of the far more elegant JSON REST model. I am usually working in ASP.NET and IE DOM client land, thankfully with the prototype.js library. So the question of the day was what would REST JSON access look like from UniBasic and what are the central issues in using it?

Well in addition to all the SOAPRequest support added with the UniBasic Extensions are a couple of simple little commands for retrieving the content from a URL. Now URLs often return HTML intended for human consumption and are not very easy to parse. The JSON REST model however returns very nice orderly string serializations.

With something as simple as:

CREATE.REQUEST.RTN.CODE = createRequest(URL:"?":QS,HTTP.METHOD,HTTP.REQUEST.HANDLE)
SUBMIT.REQUEST.RTN.CODE = submitRequest(HTTP.REQUEST.HANDLE,'','',HTTP.RESPONSE.HEADERS,HTTP.RESPONSE.DATA,HTTP.RESPONSE.STATUS)

you can get orderly responses like:

HTTP.RESPONSE.DATA = {'oValItem':{'FileName':'CM', 'TableNbr':'', 'ItemId':'1024
', 'NewItemId':'1024', 'Valid':'True', 'Display':'Sears Systems, Incorporated',
'ErrorFlag':'False', 'ErrorMsg':''}}

UniData does not yet include a JSON DOM to match the XML DOM of the UniBasic Extensions, but the parsing difficulties of JSON serializations are relatively minor. And if you need to directly access JSON REST services in the middle of UniBasic code this seems like nice direct approach.

The real stumbling blocks aren't coding difficulties, but security of your application server if you open up the HTTP or HTTPS ports for UniBasic to "see" services on the Internet. One answer to this is to go through a proxy server. This also points out one of the benefits of the architecture of Manage 2000 with its ASP.NET arm which may be used in a similar manner to delegate interaction with the messy world away from your closely guarded business database into a DMZ area.

Thursday, September 2, 2010

Dynamic Dropdowns and TM JSON arrays

Whether you call it RIA or Web 2.0 or AJAX enabled, there is an evolutionary process taking place on the web these days with UIs morphing from BLOCK-TERM like full page postbacks to much more granular dynamic changes in the page as the user interacts with it. And you can certainly see the effects of this progression in Manage 2000 release 7.3.

A developer friend asked if I preferred coding in VB in the code-behind ("code-beside" in the new parlance) or on the client using javascript and such. This is one of those questions that makes my brain churn for awhile.

The central conclusion I finally came to was that if it makes the UI more convenient for the user, that I dynamically change control configurations (like reloading drop down options based on previous answers they've selected) as they progress through a page then I prefer taking manual control on the client using javascript. I still use code-behind and aspx templates to push the HTML out in the first place, but then shift to javascript-AJAX-JSON-DHTML to make the user interface reactive and dynamic so that it responds more intelligently to the conversation that the user is having with it without having to pause and reprocess major Page construction code.

So how does one go about dynamically loading an HTML SELECT with options from a Manage 2000 TM table?

I have added a new service called GetTMTable in /mt/JSONServices for just this purpose, using much of the same code as my last post:

Private Function GetTMTableAsJSONArray(ByVal context As HttpContext) As System.Text.StringBuilder
Dim TableNbr As String = context.Request.QueryString("TableNbr")
Dim result As New System.Text.StringBuilder
Dim arTableEntries As New System.Collections.Generic.List(Of Array)
Dim ds As New ROISystems.Components.roiDataSet
Dim TableMaster As New ROISystems.WebControls.roiTableMaster
ds = TableMaster.GetTable(TableNbr)
For Each entry As DataRow In ds.Tables("VALIDATION_Validation_Info").Rows
Dim row() As String = {entry.Item("Code"), entry.Item("Desc")}
arTableEntries.Add(row)
Next
Dim JSONSerializer As New System.Web.Script.Serialization.JavaScriptSerializer
result.Append(JSONSerializer.Serialize(arTableEntries))
Return result
End Function

Here is the js portion of my testcode.
function LoadTable() {
var Site = document.location.pathname.Field('/', 2, 1);
var svcUrl = document.location.protocol
+ '//' + document.location.host
+ '/' + Site + '/MT/JSONServices/GetTMTable.ashx';
var TableNbr = $F('TableNbr');
var qs = 'TableNbr=' + TableNbr + '&Cid=' + $F('hedtcid');
new Ajax.Request(svcUrl + '?' + qs, {
method: 'get', asynchronous: false,
onSuccess: function(transport) {
var arTableEntries = transport.responseJSON;
// clear and reload the dropdown with the new table
$('ddlTMTable').options.length = 0
$A(arTableEntries.each(function(item) {
var opt = document.createElement('option');
opt.text = item[1];
opt.value = item[0];
$('ddlTMTable').options.add(opt);
}))
}
});
}


The result is blindingly fast reloads of the dropdown list from various tables.

When you do finally postback you will run into some MS security checking unless you disable event checking in the page declaration in the aspx file, or in a web config setting:
@ Page EnableEventValidation="false"
or
pages enableEventValidation="false"

You may also run into occasional confusion during postback on the part of webcontrols code trying to figure out why what is coming back doesn't match what was sent out. To avoid this confusion you can either just use plain ol HTML controls or check the Request.Form("lbID") array directly if it gets to be a problem.

Unfortunately I had to change the roiTableMaster control to remove a dependency on roiPage so that it would work out of JSONServices. This makes it difficult to patch, but it is all better for 7.3 sp2.

In the mean time you could, of course, implement a version of /mt/JSONServices that descends from roiPage rather than IhttpHandler, it just would have all the application overhead that roiPage carries around.

Wednesday, July 21, 2010

JSON Conversions

So, I am integrating a provided web page into a Manage 2000 site and I need to supply this external page with a JSON array of data on the querystring based on the contents of a Manage 2000 TM Table.

How to get a JSON serialization out to the client world?

There is a very nice little namespace that I have not previously run across, System.Web.Script.Serialization. And in it you will find a JavaScriptSerializer class (read JSON serializer!).

With the JavaScriptSerializer you can convert a .Net Hash to or from a JSON object, or a .Net System.Array to or from a JSON array, or a bunch of other mappings including your own.

In my case I want to end up with a JSON array of elements with each element comprised of an array of code description pairs.

Private Function GetTMTableAsJSONArray(ByVal TableNbr As String) As System.Text.StringBuilder
Dim result As New System.Text.StringBuilder
Dim TM As New System.Collections.Generic.List(Of Array)
Dim ds As New ROISystems.Components.roiDataSet
ds = TableMaster.GetTable(TableNbr)
For Each entry As DataRow In ds.Tables("VALIDATION_Validation_Info").Rows
Dim row() As String = {"", ""}
row(0) = entry.Item("Code")
row(1) = entry.Item("Desc")
TM.Add(row)
Next
Dim JSONSerializer As New System.Web.Script.Serialization.JavaScriptSerializer
result.Append(JSONSerializer.Serialize(TM))
Return result

End Function

Yes, the JavaScriptSerializer is my new favorite toy for transforming data during client side AJAX activity.

Friday, July 9, 2010

Hyper Activity

A recent treasure from our 1st live Manage 2000 7.3 site led me back to researching performance issues in the Pegged Detail page of ItemActivity. This has been a long standing issue in the field for certain customers on certain parts under certain circumstances.

Previous optimizations have included converting dynamic arrays to dimensioned arrays when handling the TD_ITEM_PEG_ACT_RESULT file items. But even with attributes stored in separate dimensioned elements the immense number of values that may be generated in the real world overwhelm the UniData box and then moving the gigantic business object to the web server overwhelms that box. The resulting user experience "just sucks".

The real detail work of analyzing pegged detail is done in SUB.BUILD.ITEM.ACT.PEGGED, and entails building a number of attributes for each detail and sorting by date and by a peculiar transaction type order. And there is no good way of separating the selection and sorting of keys from generation of detail data as described in the PAGED.BTO document because the whole point is to keep a running total of availability and a number of calculated and summarized values.

To meet these requirements and scale to many thousands of lines of detail I created a process work file keyed by date and by sequence number and wrote simple flat records for each detail. Then SSELECT the work file and update the small simple records with the running total fields. This replaces a LOCATE and INSERT loop that hockey sticks as the the value count exceeds a thousand. And finally write the results in pages to the WEB_COOKIE_DATA file where each page record only contains, say 25 (users current items per page preference) values for each attribute.

The business object returns only the 1st page of pegged detail and the cookie where the rest of the pages may be found. The web page may then use another business object to read any page of the result the user wishes to view.

Where the business object is handling 3000-4000 details the whole-view multi-valued set approach was taking 6+ seconds on a modern Itanium UNIX box. The work file approach reduced this to less than 1/2 second. And testing shows a performance curve up to 20,000 details running at about 7,000 details per second on our development box.

On the web side the performance improvement is even more dramatic as the dataset and grid processing on large local datasets just overwhelms the web server processor. Eliminating the large business object result, and asking the web server to only create objects representing a single page of the pegged detail, results in near instantaneous page changes even with the trip back to the application server to pick up the page data.

Conventional wisdom says memory is fast, disk is slow. But in this particular scenario it is much more efficient to create a work file, populate it, SSELECT it and work with small flat dynamic arrays than to follow the standard path and attempt in memory sorting of large deep dynamic arrays.

Thursday, June 3, 2010

Sub Valued Level Prompting Details

How do you fixup the screen and do cross referencing and other validations when you use SUB.VALUE.PROMPTING on a SUB.MT500 screen?

Here is an example where we want to prompt for multiple file names and within each file for multiple item ids.

Instead of calling SUB.VALUE.PROMPTING directly from the SCREEN, call your subroutine which in turn will call SUB.VALUE.PROMPTING.
















Remember to add right justified fields using the PWS screen in SCREEN.BUILD if you want the count of sub-valued items to line up nicely.


You can add logic to execute only at the sub-valued level by checking X_Data_2 for the FROM.SCROLL.MAINT flag.

*

2200* Before prompt logic for Item_Attachments

*

IF INDEX(Prompt.X_Data_2, 'FROM.SCROLL.MAINT', 1) > 0 THEN

* Handle inner event from SUB.VALUE.PROMPTING execution of SCROLL.MAINT

* Reset SIP.VAL.FILE to enable cross referencing

FILE.VALUE = FIELD(Prompt.Display_Text_2, ",", 2)

FILE.LIST = MAIL.FILE.File_Attachments

CURRENT.FILE = FILE.LIST<1,FILE.VALUE>

CALL GET.DB.FILE(SIP.VAL.FILE, CURRENT.FILE)

ERROR = 0

RETURN

END

* Handle MAILBOX.ATT prompt event

GOSUB 2210;* Fixup Prompt Label with SUB.VALUE level display text

* Setup XREF stuff

Prompt.Conversions_Edits = '0X':@SVM:Prompt.Conversions_Edits

* remove compiled edits to force recompile

Prompt.Conversions_Edits = FIELD(Prompt.Conversions_Edits,roiDataMark4,1)

CALL GET.DB.FILE(SIP.VAL.FILE, CURRENT.FILE)

CALL SUB.VALUE.PROMPTING(ANSWER,SUB.DATA,P.NBR,PMT,SAVE.FN,VALUE)

END

*

RETURN

If you want to fix up details like the screen labels and prompt text while at the sub-valued level you can do this sort thing:

*

2210* Display SubValue Level Label Text

*

SET.NBR = Prompt.Scroll_Field_Type[2,2]

LOCATE SET.NBR IN SCROLL.DATA<9,1> SETTING PRIMARY.IDX ELSE RETURN

PRIMARY.PMT.NBR = SCROLL.DATA<1,PRIMARY.IDX>

PRIMARY.PMT = PID(PRIMARY.PMT.NBR)

STARTING.ROW = PRIMARY.PMT<1,7>

HEAD.ROW = STARTING.ROW-1

DISP.MASK = "L#":Prompt.Display_Length

ID.LABEL = XLATE("DICT ":CURRENT.FILE, "F0", 61, "X")

IF ID.LABEL = "" THEN ID.LABEL = "Item Id"

LABEL.TEXT = FMT(P.NBR,"2\0R"):".":VALUE:' ':ID.LABEL

Prompt.Text = "Enter ":ID.LABEL

REDIS.MISC<10> = L(HEAD.ROW):C(PMT<1,6>):LABEL.TEXT DISP.MASK

PRINT REDIS.MISC<10>:

RETURN

For Validation after prompting you can check the X_DATA_2 flag again and if you are at the multi-valued level simply request a repaint with ERROR=1000, but at the sub-valued level actually carry out input validations programmatically. Remember that your sub-valued level validation logic is being called from SCROLL.MAINT so do not use ERROR codes like 200, 1000 as you would for SUB.MT500, simply 0 or 1.

*

3200* After prompt logic for Item_Attachments

*

IF INDEX(Prompt.X_Data_2, 'FROM.SCROLL.MAINT', 1) = 0 THEN

* Remove SV Label Text

REDIS.MISC<10> = ''

ERROR = 1000

END ELSE

IF NOT(SIPDATA) THEN RETURN

FILE.VALUE = FIELD(Prompt.Display_Text_2, ",", 2)

FILE.LIST = MAIL.FILE.File_Attachments

CURRENT.FILE = FILE.LIST<1,FILE.VALUE>

REC = XLATE(CURRENT.FILE, ANSWER, -1, 'X')

IF REC = '' THEN

* Message 502: %1 item %2 not on file

CALL SCREEN.MSG(GetMessageText(502,CURRENT.FILE:@VM:ANSWER,0):";H;#M446065")

ERROR = 1

END

END

RETURN

Wednesday, March 17, 2010

The Wonderful World of Wizards

It has been an exhilarating and somewhat exhausting spring here in Minneapolis; from 2+ feet of snow pack to clear yards and 60 degree sunshine in 2-3 weeks. Getting beta's underway has not been fast enough or clean enough for my impatient expectations. But that's why you have betas to find the stumbling blocks. While waiting to enhance as-yet unidentified pre-enhancement conditions, I have been working on a pet project to create web function wizards with more specific generation capabilities.

My first re-visitation to IWizard has resulted in a modest little wizard that will help you generate a HyperQuery web function. The HyperQuery control allows configuration of a REPORT.BUILD like query based web function.

The second undertaking turned out to be a lot more interesting and a lot more work. The BTO Inquiry Wizard allows you to select a business object, select from its available fields and generate a working inquiry with all the data access components and controls configured, and with a FormView containing single valued fields labels and textboxes, and GridView controls for each set.

The great part about wizards is, of course, that you can take the results and enhance the heck out of them. They provide RAD starts to developing your own web functions while still leaving you in total control.

Wednesday, October 7, 2009

Summer's End 2009

Perspectives session material was due in last Friday. Now I can get back to building new stuff. I still haven't finished the display panel memory. It is a small annoyance, but a general client side context persistence mechanism will come handy over and over.

There also remains some fleshing out of the metadata implementation. In particular being able to set an M2k edit pattern and have a Regex generated and applied to the input would be sweet. I'd also like to get any existing pattern matching or required validators to be automatically tied in...December is rapidly approaching, we'll see what we can sneak in. The performance exception logging and user preference extensions are a must, so I guess I better start on those directly.

I just put in a replacement for the dorky alerts with which I was displaying ?3 and ?5 help. Now we have a nifty m2kShowMessage(hmtlMsg) js function with which it is easy to compose an HTML display and pop it up using an in-line div that won't set off the pop-up blockers.

Thursday, July 16, 2009

Manage 2000 Web 2.0

No, I don't know what it means either. But I do think the web user experience on Manage 2000 7.3 is going to be noticeably and, in many cases, dramatically improved from the 7.1/7.2 technology. It seems to me like the we are passing through the 3rd generation up the web UI S-curve. With release 7.0 we dabbled with asp script based web infrastructure. We've had a good run with Visual Studio 2003 and ASP.NET 1.1 as a basis for Manage 2000 7.1/7.2 releases. And now with release 7.3 we are working on top of ASP.NET 3.5 using VS 2008 with more current infragistics controls and a much more robust client side javascript infrastructure library roiGlobal.js with deeper support from the Prototype.js, which underlies many other clientside toolsets such as Ruby-On-Rails and Scriptoculus.

Every project brings with it opportunities to enrich the roiGlobal.js library and move more time sensitive user interactions to the client-side world of javascript, D-HTML, JSON, and AJAX. Yet even as I explore these new environments and tools I find myself re-creating patterns from the PWS / SUB.MT500 world. There are few things as elegant and powerful as the declaritive UI specificaton for a computer prompting a user which is called the PID in M2k geekspeak.

Following that pattern Manage 2000 7.3 web pages all have the equivalent of PID available. It is defined in roiGlobal.js based on a Prototype.js class called a Hash. It's name in nvcMetaData, and it is to the web function what PID(40) is to the PWS function. That is to say that dynamically altering the metadata item for an HTML textbox effectively controls its behavior. Thus, application behavior can be achieved by setting properties of the metadata instead of having to code up all the necessary javascript.

Friday, December 12, 2008

Visual Studio 2008 and Manage 2000 Web Functions

Mark your calendar 13:58 on December 12, 2008 the development staff completed the last of the 295 ASP.NET web projects, thus heaving the Manage 2000 7.3 web footprint on top Microsoft's not-so-backwardly-compatible Visual Studio 2008 designer.

This is an important achievement in terms of product life expectancy as it puts the Manage 2000 web presence on supported Microsoft technology for at least the next 5 years.

Hopefully, next time they will be a little kinder and gentler with backward compatibility.

This is not the end of development for the 7.3 release. It is not even the beginning of the end, but merely the end of the beginning,to paraphrase Carnahan  paraphrasing Churchill.

Next up is build and install capability so that as developers add and enhance application functionality we can easily re-install development sites and do integration testing.

Then the real fun begins putting to good use all of the fantastic enhancements in ASP.NET 3.5 and Infragistics 2008 to make Manage 2000 7.3 web functions sizzle.

Monday, February 12, 2007

7.2 Release and Looking Ahead

Entry for February 11, 2007

After 10 days of negatives temps, some days with -5 for a high, I am ready to be out of the deep freeze. Past -10 my car sleeps with a trouble light under the hood to make sure I can get the kids to school in the morning. Maybe time to add that block heater.

The 7.2 release is currently in Beta. Lots of positive feedback, as well as, bug reports and clean-up work.

We are currently conducting training presentations to helpline, custom code and consultants. Last week I covered PageViewFilters and other tools enhancements, as well as, the new web ProductConfigurator interperter for implementing CTO on the web.

I continue to scout out the direction ahead with Visual Studio 2005 and ASP.NET 2.0. The way looks rough but passable at the moment, though where we all end up is sure to be filled with complaints. There are some tools I can write to smooth the way, but there are significant short-comings in the VS2005 IDE. The VS team seems to have fixated on codeless access to SQL and forsaken all other paths. Everything is focused on stateless, first-normal data updates between flat UI components and the SQL database, or some other object which must be organized along the same CRUD lines in a first normal world. They do not appear to have made any accomodation for web base applications with rich hierarchical complex UI's. Apparently we are supposed to use winforms unless we are implementing an Amazon.com web site. There are web sites and then there are web applications. Microsoft appears to be pressuring the web application camp back to winforms.

In particular databinding UI from hierarchical datasets to webcontrols is just plain gone. They have drastically different models for winform data access as compared to web forms. They are pushing ObjectDataSource and SqlDataSource as codeless access to databases and great productivity enhancements. But these depend on new TableAdapters within the dataset to orchestrate flat table accesses back and forth to the DB. They are tightly coupling the UI to the DB.

For thoses of us with ACID processes based on hierarchical datasets there is no room to fit in. The idea that first normal database requirements are going to start driving UI design is just plain scary. It may be ultra productive for the person creating it, but it's going to be butt ugly for the poor soul who has to actually use such software.

On another front I am watching Orcas with amusement. They are so hyped about LINQ and the ability to hook up SQL query statements to gridview. Manage 2000 has been delivering web query capability for over 5 years which include embedded sub-tables and hyperlinking. Our queries can even be defined from a report generator UI. I predict that when LINQ gets to field everyone will discover there is still a lot of problems because they will often want a three dimensional query result and you can't get that from a first-normal database without a lot of effort.

The reign of quality fights for a hierarchical object oriented UI. The reign of quantity fights for simple flat table models. Who will win?