A Responsive Google Map Application Template

At work, I am working on rewriting a bunch of maps so that they are consistent and mobile-friendly (a.k.a. responsive). When I starting searching for some basic how-tos I turned up nothing. I did find how to make Google Maps itself responsive. My issue was how do I add a legend and form that will filter or add content to the map and keep it mobile-friendly. Since I couldn’t find anything, I set forth to make something myself. In one weekend, I created a basic framework utilizing Bootstrap, the following week I refined and finalized the application and I think it turned out well.

I made the template open-source so that others can benefit from what I learned and help make this a better template in the future. I hope others can help me make this even more accessible and improve the template further.

I used Bootstrap because that is what I am most familiar with. In the future, if I have time, I want to try to eliminate Bootstrap to reduce the overhead. Once our new website launches, I’ll add some examples of what I created with the template so you can see why I felt this was important.

Here is what the large-screen version looks like:

Large screen view of application
Large screen view of application

Here is the mobile views:

Mobile View of Application
Mobile view of application
Mobile View of Legend
Mobile view of legend
Mobile View of Filter
Mobile view of filter

Do Not Use Comments

This came across my Hacker News feed the other day, “I’ve never seen a language’s style guide recommend avoiding comments before.” After reading the link it became clear that the HN title was link bait. What the Haskel documentation was stating is something any good programmer already knows. Don’t document every line of code.

x = x + 1; // Increment x by 1

This comment is not required as it is obvious what it is doing. My rule of thumb for commenting is; if it’s complicated or was problematic, it requires a comment explaining the why or why not. A good programmer should be able to read through the simple code and know what is going on. However, commenting on why you wrote something the way you did would help yourself or the next guy to know what you were thinking.

This comes to light not only when I am looking at code I wrote 5 years ago, but recently I had a co-worker leave. He didn’t write many (read any) comments in his code. Now in some cases I am running debug just to see what the application is doing before I can think of fixing that bug or making that change.

So do your future self or co-worker a favor and comment… but not too much.

Now monitoring my websites with Google Spreadsheets and Pushover

I used a previous version of this free monitoring tool, but I recently setup their new version. It is quite neat. Basically, it uses Google’s infrastructure to constantly hit your website and if it doesn’t get a 200 status back, it will contact you via email or SMS saying your site is down. While this was nice, I really wanted to use Pushover more. So I looked through their API and discovered it is very easy to send messages. So I modified their script and now I get notifications via Pushover. Here are the changes that you need to perform.

  1. Add two new rows after the SMS notifications row.
  2. Call the title’s what you want, but I went with “Pushover API Token ::” on A5 and “Pushover User Key ::” on A6.
  3. I created a new Pushover app
  4. I put the new app’s API token in B5 and my user key in B6.5. Go to Tools | Script editor…
  5. Then add the following code to the function logMessage(url, message) funtion in the script after the email portion:
    if (sheet.getRange(“B5”).getValue() != “”){           var options = {               ‘method’: ‘post’            };    var url = ‘https://api.pushover.net/1/messages.json?’     + ‘token=’ + sheet.getRange(“B5”).getValue()    + ‘&user=’ + sheet.getRange(“B6”).getValue()    + ‘&message=’ + alert    + ‘&title=’ + “Site ” + message;    var response = UrlFetchApp.fetch(url, options);        }

Controlling a Epson TM-T20 on the IBM i (or iSeries)

The past couple days I was on a mission to look for a way to replace our aging dot-matrix receipt printer. We got some new printers to go with our new credit card processor. These printers are Epson TM-T20 thermal printers. I had some problems finding the information to have the printer cut the paper and to get it to eject the cash drawer. I am documenting what I found here for people in the future to figure out.

You need these codes embedded in the spool file. The first hex character, 03, tells the OS we are sending ASCII characters. The second, 06, tells the OS that the next x characters are ASCII.

  • To cut the paper – X'03060D0A1D564220'
  • To eject the cash drawer 1 – X'03051B703040F0'
  • To eject the cash drawer 2 – X'03051B703140F0'

Add a custom IBM i library list to a C# application

Ever find that you need to add a custom library list to a C# application that works with IBM i data? Here is what you need to do. Add LibraryList= to your connection string. So that would look like:

connectionString="DataSource=127.0.0.1;DefaultCollection=MYMAINLIB;Naming=sql;
UserID=USERNAME;Password=PASSWORD;LibraryList=MYMAINLIB,LIB2,LIB3;"

Creating a better emailed statement in RPG using CGIDEV2

We had some… problems with our utility bill emails. They weren’t worded clearly enough that some people were getting confused. See, when I wrote this, used the only approach I knew and manually assembled a string of text until the email was created (depended upon if they were auto-pay or not), but over all the emails were very similar. We like to be customer friendly, so we decided we better make the emails clearer.

After working with our communications department, they came up with a very good set of emails… that don’t work well with how I am currently assembling the emails. Damn!

So, I walked back to my desk feeling like “Oh crap! I have a lot of changes to do.” Then I had the thought that too bad I can’t template… oh yes I can! CGIDEV2 does that! Since I haven’t done much in that field, I figured I would hit up the midrange.com WEB400 community to see what else there is out there. I came back with a list of a few frameworks, but most people basically said that CGIDEV2 was the best place to start. One helpful person, gave me the following list:

I decided to use CGIDEV2 as that was the most promoted package. After I had it installed, tried to figure out what I needed in order to do what I want. Here is the minimal information that is needed:

First you need to add the following two copybooks to your D-specs:

/copy CGIDEV2/qrpglesrc,prototypeb
/copy CGIDEV2/qrpglesrc,usec

Then in my loop I did the following:

dow (not %eof());

// Clear the buffer to make sure it is empty.
callp ClrHtmlBuffer();

// Pull the correct message.
if (autopay());
callp gethtmlIFS(‘/utilityBillText/UtilityBillAutoPay.txt’);
else;
callp gethtmlIFS(‘/utilityBillText/UtilityBillRegular.txt’);
endif;

// Update the variables in the message
callp updHTMLvar(‘custnum’:%editw(CustN:’0 – ‘));
callp updHTMLvar(‘serviceaddr’:%trim(Adrs));
callp updHTMLvar(‘balance’:%char(balance));
callp updHTMLvar(‘duedate’:%char(%date(DueDt):*usa));

// “Write” the file into the buffer
callp wrtsection(’email_body’);
// Save the file to the IFS in the tmp directory.
rc = WrtHtmlToStmf(‘/tmp/emailbody.txt’);

// Send email through MailTool (http://bvstools.com)
SendMailAttachment(fromAddr:
%trim(toAddr):
”:
”:
subject:’*ATT’:’/tmp/emailbody.txt’);

read FILE;

enddo;

Suprisingly this worked the first time! Now if the communications department ever wants to change the email, all I need to do is update the text and the email is now changed.

I hope this helps others that are looking at doing this.

How to Debug a Batch Program

Via BVS/Tools – FAQ

How do I debug an RPG program in batch?
Submit your program into batch and make sure it is held. Do this by either holding the job queue that you are submitting the job to, or use the HOLD(*YES) option on the SBMJOB command.

  • Use the WRKUSRJOB and display the job you with to debug with the display job option (5).
  • Write down the user name, job name and number.
  • Start a service job using STRSRVJOB entering the name, job name and number from the previous step.
  • STRDBG PGM(YOURPGM) – Press F12 to exit the source display (Sorry, can’t enter breakpoints yet).
  • Release your submitted job by releasing the job queue or the job itself.
  • A display will appear asking you to press F10 function key. Press F10 and you will be brought to a command line.
  • Enter DSPMODSRC and enter your breakpoints.
  • Leave source display and command line by pressing F3 until you are back to the screen that asks you to press F10 to enter breakpoints.
  • Press Enter to start your job.
  • After that the job begins running and stops at the first breakpoint reached.
    Also, you can create a simple CL to combine the two commands to make it easier to use. I created a command STRBTCHDBG so it is really simple to use. I might post the source to that later.

Chili Bread Bowls

I have been loving the Artisan Bread in 5 Minutes series of books so today when we thought that making bread bowls and chili sounded good I went to my bible of bread.

To start with, make your own favorite chili and let it simmer all day in the crock pot (because it tastes better that way).

I then turned to the original Artisan Bread in 5 Minutes a day book. I choose the master recipe, the boule since it was easy to work with and I knew it had a nice crust to prevent it from loosing shape when the chili was added. I tried several sizes of loaves to see what size worked the best. We discovered that while the 1/2 lb loaf was too small to hold more than 1 scoop of chili, it was just the right size for a complete meal.

Loaves of bread cooling

The 1/4 lb loaves were way too small. The 3/8 (or so) lb loaves were just right for the kids.

All I did was take a sharp knife and cut a circle in the top pushing the blade most of the way in. Then pull out the bread until it was a good bowl. I did nothing special on the inside beyond that. Now you can see the finished product.

The only problem? It takes so long to bake enough bread if we were to have more guests!

How do I iterate over a set of records in RPG with embedded SQL?

A person on StackOverflow asked a good question a while back.

How do I iterate over a set of records in RPG(LE) with embedded SQL?

This is a mix of information gleamed from many sources. I use a standard template for every program and modify as needed from there.

Here is the core of the program I use for SQLRPGLE programs:

       // SQL Fields
     D sqlStatement    S           2048A   varying
     D SqlResult       DS                  qualified
      /copy modules/qcopysrc,sql_h

       //*************************************************************************
       // Internal Subprocedures
     D Init            PR
     D Main            PR
     D BuildSqlStatement...
     D                 PR
     D OpenCursor      PR             5A
     D FetchNext       PR             5A
     D CloseCursor     PR             5A
       //*************************************************************************
       // Entry Parms
     D T_RPT_SQL       PR                  extpgm('T_RPT_SQL')
     D T_RPT_SQL       PI
       //*************************************************************************
      /free
       Init();
       Main();
       *inlr = *on;
      /end-free

     P*--------------------------------------------------
     P* Procedure name: Init
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P Init            B
     D Init            PI
      /free
       exec sql
         set option naming = *sys, commit = *none, usrprf = *user,
                    dynusrprf = *user, datfmt = *iso, closqlcsr = *endmod;
       return;
      /end-free
     P Init            E

     P*--------------------------------------------------
     P* Procedure name: Main
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P Main            B
     D Main            PI
      /free
       BuildSqlStatement();
       if (OpenCursor() = SQL_SUCCESS);
           dow (FetchNext() = SQL_SUCCESS);
               // *** INSERT CODE HERE ***
           enddo;
       endif;
       CloseCursor();
       return;
      /end-free 
     P Main            E

     P*--------------------------------------------------
     P* Procedure name: BuildSqlStatement
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P BuildSqlStatement...
     P                 B
     D BuildSqlStatement...
     D                 PI
      /free
       gSqlStatement = ' ';
       return;
      /end-free
     P BuildSqlStatement...
     P                 E

     P*--------------------------------------------------
     P* Procedure name: OpenCursor
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P OpenCursor      B
     D OpenCursor      PI             5A
      /free
       exec sql
         close c1;
       exec sql
         prepare sel from :sqlStatement;
       if sqlStt = SQL_SUCCESS;
         exec sql
           declare c1 cursor for sel;
         exec sql
           open c1;
       endif;
       return sqlStt;
      /end-free
     P OpenCursor      E

     P*--------------------------------------------------
     P* Procedure name: FetchNext
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P FetchNext       B
     D FetchNext       PI             5A
      /free
       exec sql
         fetch c1 into :SqlResult;
       return sqlstt;
      /end-free
     P FetchNext       E

     P*--------------------------------------------------
     P* Procedure name: CloseCusor
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P CloseCursor     B
     D CloseCursor     PI             5A
      /free
       exec sql
         close c1;
       return sqlstt;
      /end-free
     P CloseCursor     E

Let’s break this down some. First, the copybook sql_h defines some standard SQL return statuses including SQL_SUCCESS (which is 00000). My mainline has three lines of code:

  Init();
  Main();

  *inlr = *on;

This is for when using RDi and WDSc outlines. I can quickly jump to any portion of the code. You can’t jump to the beginning of the mainline code that I know of in the outline.

Next, we have the BuildSqlStatement() subprocedure. I used this mostly for when I need a dynamically built SQL statement. I hope OpenCursor(), CloseCusor(), and FetchNext() are self-explanatory. One thing I want to note, I realized that SQLSTT is a global value but if you return it in the subprocedure, you can then do shorten the calling code to if (OpenCursor() = SQL_SUCCESS);.

So this is how I iterate over a set of records using SQL in RPG. Is there a better way? I am open to suggestions!

If you like this article, please up-vote my answer.