Tetra Information Technology
 
Paul Smith Matt Smith Heather Keene Old Code Programming UI Dev Sandbox

Paul C Smith
(click for chronological resume)

Software Developer/Oru Kayaker. Every keystroke at this site came from me. I'm the sole proprietor.
Employment Blog
Front/Back End Web 2.0 PWA developer.
Troubleshooter
Lifecycle Documentation
Blackberry Island Series: 2 Hours North | 12 Minutes West |

Availability and Best Way to Contact:

Paul is available for remote work or onsite work local to Orange County CA. Not considering other locations.
USA Citizenship
Best way to contact: tetraInfoTech@gmail.com
I get lots of calls and emails from all over the planet every day. Few are a fit. Use email. Do NOT phone please ... its on DND.
Please put summary info in Title; for example: 1099 Go FullStack in Seattle GOOG, FTE FrontEnd Redmond MSFT, w2 Contract PreactJS Tetra Tech Bellevue.
Then, list the rate to be paid. Standard rates appear below.
For immediate consideration, include the name/acronym of my company. Plural is ok too :-) ....

Rates: 1099: $85/hour; W2 Contract: $75 per hour onsite, $60 remote.

Wish I could: Write a PreactJS UI for a Loihi Neuromorphic Computing Project, with a service worker talking to a Golang API endpoint. Lately, doing a native JavaScript UI implementing the Bluetooth API, storing locally and sending telemetry to MongoDB Atlas Data API.

Highlights From a Diverse Career:

Technical Skill Summary

Front End: PreactJS is my current favorite. Ready-made for PWA. Tiny, pages dance and a pleasent syntax. Historically am a Web 2.0 SPA client-side dev type ... tools are object-oriented dynamic JavaScript, AJAX/ XHR, CSS/SASS, HTML5, JSON, JSONP, AngularJS, jQuery, and DOM-native.

Backend Persistence: SQL, NoSql, Entity-Framework, Object

Backend Languages: Golang. JavaScript 1.7, JScript, Java, LotusScript, C# (novice)

Payload Architect: Use Cases, Interface Specs, Functional Specs, REST Payload Optimization, Presentations, User Manuals

Mark-Up: XML, XPATH

Source Control: Git, Team Studio, Subversion

Embedded: C, Unix/Linux, NewtonScript, WINce, Assembly 8051/68000/Z80

Protocols: TCP/IP, TLS, FTP

Other: Expert Systems, Neural Networks (aka Machine Learning), Hardware Design

Education

Recent IDEs: Visual Studio Code, Huron Click, Visual Studio, Go IDE, JetBrains, Playframework, Eclipse, Notepad++

Soft Skill Summary

  • Writing: Use Case, Specifications, Bid & Proposal, White Papers, and Technical Manuals.
  • Customers: Technical Support and Resolution
  • People: Group/Team Lead, and Technical Program Manager (28 reports).

Older Skills

  • Hardware Design: Analog and Digital Hardware
  • Niches: Master troubleshooter

Matt Smith

Computer Technician, Network Administrator, Server Administrator

He can be contacted directly at tetrambs@gmail.com.
Education: B.S. Bellevue College 2020, A+ Certification, several Microsoft Certifications

Heather Keene

GIS Technician, Photo Analyst, Data Entry Tech, QA

Details. Heather does them well.

Snippets of code from Antiquity...

A simple JavaScript Ajax demo

This sample demonstrates the use of a view object and an xhr object.

In Modern JavaScript, done with the more obvious Async/Await syntax.
Before that done with Promises.
Before that done with function Callback, often from some library.
				
   xhr = {
	//normally we would use a platform (jQuery or dojo xhr) AJAX call,    
	//but for purposes of showing how it really works, I've rolled my own 
	serviceURL: "services/experDemo.json",
	
	get: function(service,param) {
	  if (typeof window.ActiveXObject != 'undefined' ) {
	    xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");
	    xmlDoc.onreadystatechange = this.callback ;
	  }
	  else {
	    xmlDoc = new XMLHttpRequest();
	    xmlDoc.onload = this.callback;
	  }
	  //stub file for this demo
	  var url=this.serviceURL + '?' + service + '=' + param;
	  xmlDoc.open( "GET", url, true );
	  xmlDoc.send( null );
	},
	
	callback: function() {
	  //asysnchronus callback
	  if ( xmlDoc.readyState != 4 ) return ;
	  var respObj=eval(xmlDoc.responseText);
	  view.setResult(respObj);
	}
  };
				
   view = {
	getCityList: function() {
	  var zip=document.forms[0].zipCode;
	  xhr.get('zipCode',zip.value);
	},
	
	setResult: function(obj){
	  var label=document.getElementById('label');
	  label.style.display='inline';
	
	  var target=document.getElementById('CityList');		
	  target.innerHTML="";
	  if (obj==null) {
	    target.innerHTML="none found matching zip";
	  }
	  else {
	    for (i=0; i<obj.length;i++) {
	      target.innerHTML+=obj[i].cityName+", "+obj[i].stateCode+<br />'; 
	    }
	  }
	 }
  }; 
				
	
Compare with a 2020 Preact Component's componentDidMount async await fetch built into the browser
  async componentDidMount() {
	let response = await fetch(API + this.state.verb + "=" + this.state.text);
	let json = await response.json();
	if (json && json.count > 0) {
	 let rows = json.payload;
	 this.setState( { rows } ) 
	 for (var i=0; i< rows.length; i++){
	   rows[i].city = rows[i].consumer.city;
	   rows[i].make = rows[i].vehicle[0].make;
	   rows[i].rgn = rows[i].consumer.state;
	   rows[i].formerInsurer = rows[i].coverage.former_insurer;
	 }
	}
	else {
	  this.setState( { rows: [] } ) 
	}
  }
				
		

MySQL Trigger:

This sample records a history of changes to data in a ticket table into another table that records the revisions.

MySQL Trigger:

This sample records a history of changes to data in a ticket table into another table that records the revisions.

CREATE TRIGGER chg AFTER UPDATE ON tickets
FOR EACH ROW BEGIN
INSERT INTO revision
(ticketID, event, empID, dt) VALUES
(OLD.ticketID, 'MODIFY', OLD.empID, OLD.dt);
END;

An Oracle OUTER JOIN or LEFT JOIN Example:
This sample shows how to get the names for a transaction table after May 12 in descending order
even if there isn't a nameidx in the right-side fc_name table

SELECT p.empnameidx as 'Emp ID', p.rate, p.amt, p.phaseidx as 'Phase ID', n.lastname
FROM pr_trx_detail p LEFT JOIN fc_name n
ON p.empnameidx = n.nameidx
WHERE p.trxdate > '12-MAY-2008'
ORDER BY p.amt DESC


An example of a API URL that generates a Domino report from a Java Agent
This url that can be sent via email for example:
http://royfarms.com/mdwinestore/portal.nsf/report?openagent&query:[form]%3Dlot+or+([form]%3Dload+and
+[type]%3DIncoming)+and+[_CreationDate].GT._30DaysPast+and+[customerIdent]%3D98072& ....

The url corresponds to a Domino Full Text Query
[form]=lot or ([form]=load and [type]=Incoming) and [_CreationDate]>_30DaysPast and [customerIdent]=98072
that returns a tabular report ...
most folks don't realize Domino can be programmed "relationally" ... afterall, it's all just *SET THEORY*
And the Java that parsed it ...

package urlParser;
import java.util.*;

/**
* StringUtils

* A collection of useful string utilities developed in prehistoric Java days.
* @author Paul Smith
*
*/
class StringUtils{

/**
* parseURL

* Parse a set of url symbol:value pairs into a Hashtable so that it be accessed by symbol
* @param str string of symbol-value pairs; i.e. &symbol_1:val_1&symbol_2:val_2
* @param delimRow typically an ampersand &
* @param delimCols typically a colon :
* @return a Hashtable of the symbol-value pairs
*/	
public Hashtable parseURL(String str, String delimRows, String delimCols) {
Hashtable table = new Hashtable();
try 
{
 StringTokenizer tableRows = new StringTokenizer(str, delimRows);
 int numRows = tableRows.countTokens();
 int i = 0;
 final int PAIR=2;
 do
 {
     String row = tableRows.nextToken();
     StringTokenizer tableCols = new StringTokenizer(row,delimCols);
     int numCols = tableCols.countTokens();
     if (numCols == PAIR)
     { 
         String label = tableCols.nextToken();
         String content = tableCols.nextToken();
         table.put(label,content);
     }
     i++;
     tableCols=null;
 } while (i < numRows);
} 
catch(Exception e) 
{
 System.out.println("Exception thrown in parseURL");
}
return table;
} 
// ...

}





An example of some PHP code that connects to a MySQL db

<?php
//this code shows how to:
//receive input via a xhrget
//connect to a db
//determine if its an update or insert
//respond with a table of data
header('Content-type: application/json'); 
function showerror()
{
die("Error " . mysql_errno() . " : " . mysql_error());
}

//(1) open the database connection
$connection=mysql_connect("localhost","userName","password");

//(2) open connection and select binticket database
mysql_select_db("binticket",$connection);

//(3) set target table for data to be inserted or updated

$targetTable="tickets";
//(4) retreive from url of xhr get

$ticket=$_GET["ticket"];
//pad leading  zeros if necessary
$ticket=str_pad($ticket,5,"0",STR_PAD_LEFT);
//samething, but padding incorporated in input line
$emp=$_GET["emp"];
if (strtolower($emp)=="void") 
{
$emp="void ";
}
else
{
$empval=intval($emp);
$emp=str_pad($empval,5,"0",STR_PAD_LEFT);
}
//(5) see if the row with primary key ticketID already exists

$searchQuery = "SELECT ticketID FROM {$targetTable} WHERE ticketID={$ticket}";
$result=mysql_query($searchQuery,$connection);
if(mysql_num_rows($result)==0)
{
print "Inserted Data";
$insertData="INSERT INTO {$targetTable}
        (ticketID,empID)
        VALUES ('{$ticket}','{$emp}')";
}
else
{
print "Existing Data, updated if new empID";
$insertData="UPDATE {$targetTable}
        SET empID='{$emp}'
        WHERE ticketID={$ticket}";		
}
//(6) interface with the database table
if (!mysql_query($insertData,$connection))
showerror();
 
//(7) Query the database and output to the xhr handler as a table
$query="SELECT ticketID, empID, dt FROM tickets order by ticketID";
$result=mysql_query ($query, $connection);
print "<Table border=1 cellspacing=0 cellpadding=3>";
print "<TH>Bin Ticket</TH><TH>Employee ID</TH><TH>Date</TH>";
//While there are still rows in the result set, get the current row into the array $row
while ($row=mysql_fetch_array($result))
{
print "<TR><TD> {$row["ticketID"]} </TD><TD> {$row["empID"]} </TD><TD> {$row["dt"]} </TD></TR>";
}
print "</Table>";
?>

AJAX with a LotusScript Agent as the backend.

Note: even works without modification in the Domino Offline Services mode, so the Web Application functions in a disconnected environment like an aircraft.

Sub Initialize
'note the colon : char seems to interrupt the xml stream
'this AJAX agent uses the bems, list of source fields and list of target symbols
'to provide a web server for various name related pickers

Dim s As New NotesSession
Dim db As NotesDatabase
Dim doc As notesdocument
Dim qString As String
Dim thisAgent As String
Dim bems As String
Dim fieldList As String
Dim symbolList As String
Print "Content-Type: text/xml" 'xml output	
Set doc = s.documentcontext
Set db = s.currentdatabase	
qString = doc.QUERY_STRING(0) + "&"
thisAgent = takeFromFifo(qString,"&")
bems=takeFromFifo(qString,"&")
fieldList=takeFromFifo(qString,"&")
symbolList=takeFromFifo(qString,"&")
''fieldList and symbol list might look like this:
''fieldList ="EmailAddr,RepTelNbr,MsCd,RepDsgntdTtl,repnm,repnm,"
''symbolList="RegMgrEmail,RegMgrTel,RegMgrMailCd,RegMgrTtl,RegMgrDisp,RegMgr,"
Print ""	
Print getAJAXinfo(s, db, bems, fieldList, symbolList)
Print ""
End Sub


Function getAJAXinfo(s As notessession, db As notesdatabase, bems As String, fieldList As String, symbolList As String) As String 'this function will return a formatted string consisting 'of info from the rep data document as described in fieldList 'and return a keyed list as set in symbolList Dim vStatic As notesview Dim staticData As notesdocument Dim path As String Dim lookupDB As NotesDatabase Dim vBEMS As notesView Dim repDataDoc As notesdocument Dim field As String Dim symbol As String Dim fieldVal As String Dim tv As String On Error Goto ErrorHandler Set vBEMS=db.GetView("BEMS") Set repDataDoc=vBEMS.getDocumentByKey(bems,True) If repDataDoc Is Nothing Then getAJAXinfo="Error=not found" End If 'systematically find the info in the doc and tag with the symbol = value &~ tv="" field=takeFromFifo(fieldList,",") While Not field="" fieldVal=Cstr(repDataDoc.getItemValue(field)(0)) symbol=takeFromFifo(symbolList,",") tv=tv & symbol & "=" & fieldVal & "~~" field=takeFromFifo(fieldList,",") Wend getAJAXinfo=tv Exit Function ErrorHandler: Print "Error=repdataAJAX.getAJAXInfo agent at line " + Cstr(Erl) + Error End Function
Function takeFromFifo(fifo As String, delim As String) As String 'return the first item in the queue and delete it from the queue Dim takeStr As String Dim x As Integer x = Instr(fifo, delim) If x > 1 Then takeStr = Left(fifo, x - 1) fifo = Right(fifo, Len(fifo) - x) TakeFromFifo = takeStr Else TakeFromFifo = "" End If End Function

Perhaps an interesting C/C++ web demo ...

//potential web demo ... have a uav fly inside a dojo animation on the web

//then let people fly the uav over google earth
#include 
using namespace std;

// typedef allows us to give something a new simpler name
typedef struct position UAVState;
typedef struct weapon WEAPON;
typedef struct inventory INVENTORY;
//a global data element
struct position {
  float lat;
  float lon;
};
//many weapons in a in single mission's worth of UAVWeapons

struct weapon {
  int ammoCount;
  char *name;
  int damage;
}; 

struct inventory {
  WEAPON weapon1; /* struct within a struct */
  char *name;
  int missionIDent;
};

//declare prototype global functions
void info(UAVState uav);  
UAVState update(UAVState *r);
UAVState init();

int main() {

UAVState uav= init();
printf("\nPosition After Init "); 
info(uav);

update(&uav);
printf("\nPosition After Update "); 
info(uav);
//initialization should be moved to a sub

INVENTORY manifest = { 
{100,     /* onboard.weapons.count */
"GPS Guided Grenades", /* onboard.weapons.name */
16},      /* onboard.weapons.damage =16 ft circle*/
"IED Positioning Deter", /* onboard.name */
867};     /* onboard.missionID */
INVENTORY *onboard;
WEAPON *weapons;

onboard = &manifest;

weapons = &manifest.weapon1;

printf("Goal: %s\n", onboard->name);
printf("Mission ID: %d\n", onboard->missionIDent);
printf("Weapons: %s\n", weapons->name);
printf("Damage radius: %d meters\n", weapons->damage);
printf("Inventory remaining: %d\n", onboard->weapon1.ammoCount);

return 0;
}

void info(UAVState uav) {
 printf("%f %f\n", uav.lat, uav.lon);
}

//this creates and returns a new static UAVState object

UAVState init() {
UAVState uav={33,68};
return uav;
}

//this updates an existing UAVState object
//note: we pass a handle to an existing object

UAVState update(UAVState *uav) {
//for example
uav->lat =33.551667; //normally a device output read
uav->lon =68.536611;
}

Various Collected Go Recursion Snips ...

	package main
	
	import "fmt"
	import "math/rand"
	
	
	func factorial(i int)int {
	if(i <= 1) {
	  return 1
	}
	return i * factorial(i - 1)
	}
	
	func sum(numbers []int) int {
	if len(numbers) <= 1 {
	  return numbers[0]
	}
	return  numbers[0] + sum(numbers[1:]) 
	}
	
	func max(list []int) int {
	  if len(list) == 2 {
		if list[0] > list[1]{
			return list[0]
		} 
		return list[1]
	}
	subMax := max(list[1:])
	if list[0] > subMax {
		return list[0]
	} 
	return subMax
	}
	
	func count(list []int) int {
	if len(list) == 0 {
		return 0
	}
	return 1 + count(list[1:])
	}
	
	func binarySearch(list []int, target int, low int, high int) (index int, found bool) {
	if low > high { //indexes crossed over, item not found
	  index = -1
	  found = false
	} else {
	  mid := (high + low) / 2
	  if target < list[mid] {
		index, found = binarySearch(list, target, low, mid + 1)
	  } else if target > list[mid] {
		index, found = binarySearch(list, target, mid + 1, high)
	  } else if target == list[mid] {
		index = mid
		found = true
	  } else {
		index = -1
		found = false
	  }
	}
	return
	}
	
	//for a given ordinate, returns its corresonding fib number
	func fibonacciRecursion(n int) int {
	if n <= 1 {
		return n
	}
	return fibonacciRecursion(n-1) + fibonacciRecursion(n-2)
	}
	
	func quickSort(arrayz []int) []int {
	
	if len(arrayz) <= 1 {
		return arrayz
	}
	median := arrayz[rand.Intn(len(arrayz))]
	low := make([]int, 0, len(arrayz))
	high := make([]int, 0, len(arrayz))
	middle := make([]int, 0, len(arrayz))
	for _, item := range arrayz {
		switch {
		case item < median:
			low = append(low, item)
		case item == median:
			middle = append(middle, item)
		case item > median:
			high = append(high, item)
		}
	}
	
	low = quickSort(low)
	high = quickSort(high)
	
	low = append(low, middle...)
	low = append(low, high...)
	
	return low
	}
	
	func main() { 
	  i := 5 //factoral
	  fmt.Printf("Factorial of %d is %d\n", i, factorial(i))
	  numbers := []int{4,6,8,100, 101}
	  fmt.Println("Sum is: ", sum(numbers))
	  fmt.Println("Max is: ", max(numbers))
	  fmt.Println("Count is ", count(numbers))
	  target := 100;
	  index, found := binarySearch ( numbers, target, 0, len(numbers)-1 )
	  fmt.Println("Search index is", index , found)
	  //generate the sequence for the first n numbers 
	  const SLEN int = 8
	  fmt.Printf("Fibonacci Recursion:")
	  for n:=0; n < SLEN; n++ {
	    fmt.Print(" ", fibonacciRecursion(n))
	  }
	  randArray := []int { 100, 200, 30, 40, 45, 2, 0}
	  fmt.Println("")
	  fmt.Println("Sorted array is", quickSort(randArray))
	}



	
	
	

I've learned a few things over the last 30 years. These tips have worked with:

-Punch card environments

-Bare-bones assembly language environments; i.e. CPU/RAM/ROM/IO on a chip

-mini-computer terminal environments

-real-time embedded systems

-client server systems

-handheld systems

-enterprise systems


They are:

Questions and Observations

"Problems are solved by insight, yes. But there's no insight that doesn't go back to some actual experience sometime, somewhere." -- Rudolf Flesch, The Art of Clear Thinking, 1951. Robots, Apes and You.

Do New Products and Tool-Uses happen by Accident or by Design?

10 Products Discovered By Accident

I recall from my stint at Boeing that the fuel savings feature of the Boeing 787 (product trademark) were discovered accidently while searching for a way to make a subsonic jetliner. And, of course, that notorius incident from Antiquity involving the tub and Archimedes :-).

With that in mind I allow myself the freedom and time to explore, looking for the three dimensional sweet spot.

Doing it this way allows me to minimize the Activation Energy, which is key to retention.

Then comes the Creative Part.

Costly PhotoShop (registered mark of Adobe Inc) license, design agency and overly-complex design not required.


This is my playground. A couple of UI projects written in JavaScript that will solely execute in your browser, not requiring a backend. And some tongue-in-cheek Golang source code. Some test-driver code from the Grants project. For UI demos below ... right click and open your browser's Inspector. With the exception of the recent Preact project, source code is available under the Sources Tab.

Think of this page as a museum exibition of UI Code Over Time. Hover over each link.

Lately I want to skip the modern bloatware platforms and instead make applications that are small and fast.
The native DOM has grown up. Browsers have unified. NodeJS tools unnecessary. "Circling-back" as it were.
That's why I like PreactJS. Less is more. Closer to the metal, as they say.
  1. 2007. JavaScript DHTML Date Time Setter Widget Demo
  2. 2009. jQuery plugin for a canvas element button
  3. 2012. Simple notepad component using HTML5, native JavaScript and CSS3. Buggy.
  4. 2014. Angular JS Bootstrap and Local Storage Setup Demo
  5. 2016. Angular JS Circa 2020 Online Healthcare Encounter
  6. 2018. Angular JS Bootstrap Responsive Gallery
  7. 2015. Golang Interface Humor. Structs and receiver methods (Source code).
  8. 2015. Responsive Styling Demo
  9. 2018. One of many, Protractor Automated Tests (Test-driver source code)
  10. 2019. Recent: Preact Floater Demo *
  11. 2020. Recent: Preact Concentration Game *
  12. 2020. Preact Puzzle * Puzzle Component Source Code
  13. 2022. Recent: A Study in Native JavaScript, Canvas, Bluetooth telemetry, Geolocation, Offline Storage, and MongoDBAtlas Upload. Single file design pattern.
    Less is More: Single File Design Pattern.
    1. No tooling required. Fast to load.
    -No toolchain exploits
    -No toolchain knowledge required.
    -Page load times around 30 ms. Open the app, right click on Inspect and checkout the Network panel.
    2. Source code NOT obscured.
    -one file only. VS Code allows code blocks to be hidden from view with the - and plus controls.
    -readable source code is automatically compressed by GZIP; compressing twice yields nothing.
    -concerns are separated into JavaScript objects.
    -simplified troubleshooting: you always know what file has the error. Finding references between pieces is simple.
    -DOM objects are referenced by an IDENT attribute mapped into parent object in a white-listed fashion.
    -so simple, a third grader could learn to code and debug.
* Source code can be found on Github at tetrainfo