Category Archives: Uncategorized

Quarantine Diaries, Chapter 16: The Code Warrior

As the pandemic roars all around us and the U.S. is in turmoil surrounding the election, I decided it was time to return to an activity that makes the world go away for the duration, and something that focuses on what was supposed to be the main thrust of this blog:  Computer programming, the ultimate drugless out-of-body experience.

Warning: for the usual audience looking for tales of septuagenarian bicycling adventures or living with the quarantine, this is going to get a bit technical, since we’re reaching for a wider audience, fellow travelers on the road to code. But, if you are a Python programmer (or just curious about what goes on under the hood in your computer), read on.

The Code Warrior at his battle station. Browser with programming language references on the left, code editing and testing center, and requirements docs on the right, Photo captured by one of the cameras we’re seeking to automate.

As a Unix/Linux system administrator, I write a lot of scripts that are meant to be run from the command line, or embedded in other scripts to be run automatically, so providing a robust and feature-full
option list and argument list parser makes a given script more
versatile. I’ve done those kinds of scripts using the Perl language, but we’re shifting to Python now: I decided to explore a handy Python module called argparse that does just that, and includes an automatic help screen in the process, and find a use for it.

I’m relatively new to Python, having avoided it from its inception in 1991 until 2014, when I haltingly wrote a  script to operate a camera on a Raspberry Pi single-board computer, since the camera libraries were in the Python language.  It works, but I did become more interested in getting fluent in Python.  Python is more than Yet Another Object-Oriented Scripting Language: it has a distinctive style and philosophy to go with it, the Pythonic Way.  And, since languages evolve, a major dialectic shift from Python 2 to Python 3.  So, it’s time for some language immersion while we’re in the midst of the pandemic lockdown.

The core application for which this exercise is designed is a system to record from USB cameras. Since the arrival of Zoom World, where all of our organizational meetings are on Zoom, I have built up a collection of USB webcams from the local thrift store, and use them with Zoom, but would like be able to automate photo and video capture for other purposes, to augment the Raspberry Pi camera I use to monitor the driveway, recording for playback as a timelapse. Here, then, is the skeleton of my command line user interface for the USB camera project, which is inspired by, but not based on the option-rich raspistill and raspivideo programs that come with the Raspbian Linux distribution for Raspberry Pi.

This code snippet is a front-end to a larger script (not provided) that will capture output from USB webcams attached to the host system, built with the OpenCV system library, python-opencv package (for Debian-based Linux distributions), and a modified version of the acapture module.  It took a week or two of research into the OpenCV libraries and some experimentation to get enough insight into how that works in order to design a front-end command interface.  Those image-processing functions now need to be refactored into a back-end for this script, a project for next month.

The resulting system takes command-line options and arguments to control which camera to use, and whether to take or display or save camera output as a still, burst, video, or timelapse, or to display stored images as a slideshow, or single photo display. For simplicity, most of the arguments have default values, i.e., 10-photo burst, 10-second video clip, etc. so if we need a short clip or choice of several photos, we can just call the parent command without explicit arguments and get a standard output.  The script, as written, makes some assumptions about where to store the images and how to organize them, which may change as the total application is fleshed out.  As written, the idea is to store still images with file names serialized, from 0001 to 9999, as that’s a convenient way to assemble still images into a timelapse video.

The concept of having one program that serves multiple purposes makes maximum use of the feature in Unix-like file systems to be able to give a file multiple names, through hard links or symbolic links.  To use this, we make the final program executable and link to these names,  in Linux or macOS. Modifications may be necessary to run in Windows, like adding the “.py” file extension to the script names. File names aren’t hard-coded, but listed as keys in the Capabilities dictionary in the code, so this concept and construction method is adaptable to other application user interfaces, and the getargs() function and associated global constants can be plugged into any other set of programs.  As part of the design document, we list the program names and the arguments they take,

  • usbstill : take a photo, display on screen and save to disk:  ‘camera, imgdir, imgidx,
  • usbburst : take a series of photos, no delay.
    camera, imgdir, imgidx, frames (# of frames)
  • usbvideo : take a video, display on screen and optionally save camera, imgdir, imgidx, duration, scale, record?
  • usblapse : take a timelapse sequence camera, imgdir, imgidx, interval, duration, scale
  • usbslide : playback still photos as a slideshow. imgdir, imgidx, speed (0 = manual), directory
  • usbshow : display any photo resolution, path

The test harness–showHelp()–for this front-end program simply generates usage and help messages using the default values for each option: with the exception of usbshow, which requires a filepath for a  single image file, which prints the usage message and throws an exception.

Here’s the  output of the testing, using the showHelp() function to walk through the commands, with no arguments on the command line.  We’re simulating the command line in this test, so we substitute the command name for the default sys.argv[0] when calling getargs().

The main() function in the code listing below is a framework, the beginning of the main program, with stubs where the image processing and camera capture will go.  Most of that’s done, as a result of experimentation with OpenCV, but needs a lot of work and is beyond the scope of this post, which explores how to use the command line to not only tell the program what to do, but make sure the inputs are reasonable.

The main parts of the argparse module that we use are the ArgumentParser() class, the prog class variable, and the class methods add_argument(), parse_args(), and, in the test harness, print_help()

A lot of the power in Python and other programming languages is the literature–the code libraries (modules) that are either specialized extensions to the language or contributed software that are useful tools to quickly build new systems.  Python has a librarian function, pip, that accesses the master  library over the Internet to install modules not included with the basic Python installation.  Or, you can write your own or modify existing ones and rename them–Python is open source, after all.

It’s always easy enough to get a tutorial and list objects and methods for common modules in docs.python.org, but you can get a good idea of the features in a module by using the Python dir() function:  Here, we exclude the private variables and methods (which begin with an underscore character) to show the public methods.  By convention, objects are capitalized, constants  are in all uppercase, and methods and class variables begin with a lower case character:

>>> import argparse
>>> parselist = dir(argparse)
>>> for item in parselist:
... if item[0] != "_":
... print(item)
... 
Action
ArgumentDefaultsHelpFormatter
ArgumentError
ArgumentParser
ArgumentTypeError
FileType
HelpFormatter
MetavarTypeHelpFormatter
Namespace
ONE_OR_MORE
OPTIONAL
PARSER
REMAINDER
RawDescriptionHelpFormatter
RawTextHelpFormatter
SUPPRESS
ZERO_OR_MORE
ngettext

The primary object used in creating a command line parser is ArgumentParser

>>> parselist = dir(argparse.ArgumentParser)
>>> for item in parselist:
... if item[0] != "_":
... print(item)
... 
add_argument
add_argument_group
add_mutually_exclusive_group
add_subparsers
convert_arg_line_to_args
error
exit
format_help
format_usage
get_default
parse_args
parse_intermixed_args
parse_known_args
parse_known_intermixed_args
print_help
print_usage
register
set_defaults

Most of the rest of the class variables and methods are used internally by those major functions, to  perform data validation, print out usage and help, or handle errors.  add_argument() is an impressive method, providing a way to name options and arguments, constrain the data types and range of values, and make the arguments optional (with the nargs=”*” clause) or mandatory, with default values for optional arguments if not given .  Pre-validating the data greatly simplifies the logic in the rest of the program, since we can assume the input values are valid for the operations to be performed.

Here’s my code, modified slightly to fit in the page format.

#!/usr/bin/env python3
# Code

import argparse
import sys

# "Cameras" is system-specific: modify for number # of attached cameras, with /dev entries.

Cameras = ["/dev/video0","/dev/video2","/dev/video4"]
Scales = {"frames" : 0, "min" : 60, "sec" : 1}
Basecap = ["camera","imgdir","imgidx"] 
Capabilities = {"usbstill" : Basecap,
                "usbburst" : Basecap +   
                ["frames"],
                "usbvideo" : Basecap + 
                ["duration","scale","record"],
                "usblapse" : Basecap + 
                ["interval","duration","scale"],
                "usbslide" : 
                ["imgdir","imgidx","interval"],
                "usbshow"  : 
                ["resolution","path"]
                }

def getargs(myprog=sys.argv[0]):  #
    parser = argparse.ArgumentParser(prog=myprog)
    caps = Capabilities[parser.prog]
    if "camera" in caps:
        parser.add_argument('--camera', type=int, 
                            default=0, choices=range(len(Cameras)))
    if "imgdir" in caps:
        parser.add_argument('--imgdir', type=str, 
                            default='./camera')
    if "imgidx" in caps:
        parser.add_argument('--imgidx',
                            type=int,
                            default=1,
                            help="""Frame number to start: if missing or 1, files in imgdir will be deleted""")
    if "frames" in caps:
        parser.add_argument('frames', type=int, 
                            nargs='*', 
                            default=10,
                            help="numeric frame count")
    if "interval" in caps:
        parser.add_argument('interval', 
                            type=float, 
                            nargs="*", 
                            default=5.0,
                            help="time between frames in seconds")
    if "duration" in caps:
        parser.add_argument('duration', type=int, 
                            nargs="*", 
                            default=10,
                            help="Recording time, units of time")
    if "scale" in caps:
        parser.add_argument('scale',
                            type=str, nargs="*", 
                            default='sec',
                            choices=
                            ['sec','min'],
                            help="unit scale, seconds or minutes for video")
    if "record" in caps:                 
        parser.add_argument('--record', 
                            type=bool, 
                            default=False,
                            help="Save video in file: True or False (default)")
    if "resolution" in caps:
        parser.add_argument('--resolution',
                            type=str, 
                            default="640x480",
                            choices=
                            ["320x240", 
                            "640x480", "960x720", 
                            "1280x960"],
                            help="Display resolution")
    if "path" in caps:
        parser.add_argument("path",type=str)
    args = parser.parse_args()
    return(parser,vars(args))


# /Code

# Test

def showHelp(): # showHelp is a test to display the help screens
    for myprog in list(Capabilities.keys()):
        print("\n>>>> " + myprog + " <<<<\n")
        try:
            myparser,myargs = getargs(myprog)
            myparser.print_help()
        except:
            print("Error: missing required argument.")


# Skeleton for the image capture functions.
# imaging requires installation of OpenCV and 
# python-opencv
# import cv2 module
# this stub just sets up the variables needed by 
# the imaging functions.

def main():
    import os
    import os.path
    import glob

    if sys.argv[0] in list(Capabilities.keys()):
        call = sys.argv[0]
    else:
        call = "usbstill"  
        # default if running from template
    parser,argdict = getargs(call)
    keylist = list(argdict.keys())
    prog = parser.prog
    if 'camera' in keylist:
        camidx = argdict['camera']
    if 'imgdir' in keylist:
        imgdir = argdict['imgdir']
        if not os.path.exists(imgdir):
            os.makedirs(imgdir)
    if 'imgidx' in keylist:
        imgidx = argdict['imgidx']
        if imgidx == 1: 
            #delete existing files in directory 
            #if starting at 1
            files = glob.glob(imgdir + "/*.png")
            for fil in files:
                os.remove(fil)
    if 'frames' in keylist:
        frames = argdict['frames']
    if 'duration' in keylist:
        duration = argdict['duration']
    if 'scale' in keylist:
        scale = argdict['scale']
    if 'record' in keylist:
        record = argdict['record']
    if 'interval' in keylist:
        interval = argdict['interval']
    if 'path' in keylist:
        path = argdict['path']
    if 'resolution' in keylist:
        resolution = argdict['resolution']
    
    if prog == "usbstill":
        # take one photo, store
        return True
    if prog == "usbburst":
        # take frames number of photos
        return True
    if prog == "usbvideo":
        # record video for duration seconds or 
        # minutes, save if record is True
        return True
    if prog == "usblapse":
        # take pictures at interval seconds for 
        # duration seconds/minutes
        return True
    if prog == "usbslide":
        # display photos in imgdir sequentially, 
        # at interval
        return True
    if prog == "usbshow":
        # display a single image at the selected 
        # resolution
        return True
    return False  
# something went wrong--should never get here.

# /Test

Quarantine Diaries — Chapter 15: Falling Toward Fall

The year 2020 is exhausting, to say the least.  First, the pandemic, then a major health problem, then the increasingly dystopian presidential election debacle.  As tempting as it is to get mired down in politics, life goes on, even in the middle of the collapse of our civilization.  It’s been a while since we covered life in general.  In our last episode, we were engrossed in the Zoom world, which is now a “normal” part of life.

Realizing that, even though Zoom is a substitute for getting out in the world with our friends and all the organizations to which we belong, it also frees us from being locked in our house.  The excuse I used for retiring from my Montana job when I did and moving to Washington, even though I had active projects was, “Ah, that room full of servers I manage, down the hall, the room I never go into?  I can not go into that room from anywhere on the planet.”  So, I continued working, remotely, for another five years, though with greatly reduced hours.  Well, the same thing applies to Zoom.  A few weeks ago, we decided to take a break, get out of the house.  We booked a few days at the beach resort we stayed at in early March, the week before The End Of The World As We Knew It.  We left a day early, camped halfway, cold meals to avoid breaking out the cooking kit, and got in some beach walking in the early evening and a bike ride in the morning.  We grabbed some pastries and coffee at a bakery and took a longer beach walk on the way to the resort, where we jacked in to the ‘Net for our evening Zoom session for a board meeting.

The next morning, we rolled out our yoga mats and joined our yoga class back at the Senior Center in Shelton, via Zoom.  Another bike ride before the rain storms hit, so we stayed in and watched the storm the next day.  On the way home the day after, we stopped at the Cranberry Museum during a lull in the stormy weather.  When we drove by two days before, they were harvesting, but not this day, so we hiked around the bogs, in various stages of ripening and harvesting, and learned that the wet harvesting we see is only used for cranberry jellies and juices: the bags of fresh cranberries are dry harvested, but not at that location.  Washington State has a thriving cranberry industry nestled between the dunes on the coast between the Columbia, Willapa, and Chehalis rivers. It’s not big, maybe sixth or seventh behind Wisconsin, Massachusetts, New Jersey, and others, but it’s an important agricultural fixture in the coastal dunes.

After the storms passed and warm weather returned, we got busy with painting the upstairs hallway windows we replaced several years ago.  Much of the woodwork had been painted over by previous owners, so we reluctantly followed suit and painted the window frames as well.  As of this writing, I have one window to go, just the sash, with six lights that need taped, and I’m low on masking tape.

Judy has gotten back into working on her art journals, primarily making envelopes and folders to use as inserts, but it keeps her busy.  I’m spending too much time on answering computer questions.  I’ve been putting off several programming projects for lack of motivation, so I signed up for an on-line class on Python3 (I’ve been programming in Python 2 off and on for a few years, but thinking in other languages and painstakingly translating into Python, so taking a class, even one designed for beginners, is a good way to get immersed in the Python language, as I have done in the past with C, Lisp, Perl, PHP, and Ruby when I was chained to a desk all day and needed to keep busy while waiting for things to break or someone to need assistance.  My homework is probably a bit more embellished with some more advanced features than the minimum requirement, but it’s going well so far.

The programming student in class: Lecture video on right, homework and practice in center, textbooks on left. What did we do before we had wrap-around computer monitors? Oh, yes, the floor-to-ceiling bookshelves on the left.

I’m still in Physical Therapy, working on  healing up the sciatic nerve pathways and getting stronger in the right places so it doesn’t happen again.  Since we’ve incorporated our yoga practice along with the PT “homework,” Judy does them with me, so we’re both getting fitter and stronger.  Our outside exercise is still mostly walking up and down the hills of our little city, and the 8-mile bike ride two weeks ago seemed to go well, so things are looking up, though we’re reluctant to head for the bike trails in the city or ride the busy streets and steep hills around home.  The mile-age “Birthday Ride” isn’t in the cards this year, but there are other goals.  It’s always good to have goals, and back-up goals. We might count the 8-mile bike ride last month the and 8-kilometer hike last week as my 7.7 mile and 7.7 km ride/hike distances, as a consolation to the not-likely 77 km or 77 mile bike ride. But, there’s plenty left in 2020, so, who knows?  It only takes 9.6 miles or 15.5 km to log 77 furlongs, so that’s a definite possibility.

I had a bit of cooking slump for a while, but am back to baking and cooking things from scratch.  Judy likes to bake cookies, but we’re too good at eating them, and too fond of ice cream and cookies after a long walk, so our weight going into winter isn’t ideal.   So it goes: Wednesday was a baking and cooking day, Thursday was computer class homework in hopes of getting my next assignment before the weekend, which I finished Saturday night.  Friday was our virtual trip to Tacoma for the weaving guild meeting there.  We had a Zoom presentation by one of the artists-in-residence at Holly House a while back, from Holly House, and an open-air presentation this weekend, along with another Zoom weaving guild meeting next Friday.  As Calvin & Hobbes used to say, “The Days Are Just Packed.”

We rarely turn on the television.  Well, actually we do, but it’s a streaming device, so more properly, we rarely watch broadcast programming.  Instead, we watch non-network TV news on YouTube, getting programs we don’t get on our TV subscription.  Mostly, we’re hooked into the usual NPR Morning Edition and switch to the satellite NPR talk radio after, sometimes switch back to All Things Considered in the evening, while flipping through YouTube videos on our computers and phones.  We used to joke that we listened closely to the news so we’d know when it was time to head for the border, but the border is still closed, so it doesn’t matter: we have to take a stand where we are and hope we’re not first against the wall when the revolution comes.

The COVID-19 pandemic is ramping up again in many places, so we’re especially careful going out.  Be safe, be well, and maybe we’ll see some of you in 2023, if it’s safe (and permitted) to travel farther by then, and we’re all still here.

Quarantine Diaries: Chapter 14 — The New [Virtual] Reality

Chaos Central Control. Keeping track of virtual meetings calls for immersive technology.

It has been six months since the COVID-19 lockdown ended Life As We Know It.  With no Plan B, all of our activities that involved gatherings of people: school, business, entertainment, picnics, lunch meetings, dining out, and social and hobby organizations, etc., stopped.  We ordered food delivered by UPS.  We engaged with others only on social media, telephone, and email.  There was still television for entertainment, but soon devolved to reruns, as production companies shut down also.  Attempts to return to the Lost World–essentially ignoring the world-wide pandemic–failed miserably as each mass gathering resulted in a spike of new cases. Our children and grandchildren who could work remotely did, the essential medical workers carried on, and the rest scrambled for other work, switched to take-out food service, or were forced into early retirement.

But, gradually, through the spring and over the strange summer,  schools and organizations experimented with on-line learning and meeting, via video conferencing.  To it’s credit, the most advanced and popular of these services, Zoom, rose to the challenge (and the massive infusion of revenue) to meet the demand.  People of all ages, some who were still challenged by email and who eschewed social media, found themselves pointing and clicking on microphone and camera icons and struggling with volume and presentation layouts on the Zoom screens.

We belong to an organization that sponsors residency retreats for women in the arts, Hypatia-In-The-Woods.  After cancellation of most of the residencies until a suitable cleaning and turnover procedure could be developed, the retreat cottage, Holly House, reopened in mid-summer for those applicants who lived close enough to travel safely.  We lamented the loss of a venue for the artists to show their work, with the closure of the public library meeting rooms, but the program continues, with the aid of a grant to make up for lost revenue during the regrouping period.

We also belong to two fiber arts guilds, the Tacoma Weavers Guild, and the Olympia Weavers Guild, and an ad hoc art quilt study group loosely affiliated with but separate from a quilting guild in Olympia.  The Ruby Street Art Quilters (RAQ), having lost a meeting place with the closing of Ruby Street Quiltworks last year, and shifting around from Annie’s Quilt Shoppe in Shelton and fire station to fire station in Lacey, essentially went silent, though still a fleeting presence on Facebook.  There’s simply no budget, no charter, and no dues, and not a close enough association with the regular quilt guild.

The two well-funded and much larger weaving guilds, however, embraced the new virtual technologies, though slowly.  The Guilds don’t meet over the summer, but members worked behind the scenes to develop a plan to continue to thrive and survive, as both have for 85 and 75 years, respectively.  New program plans were developed to adapt to  online presentations, though most presenters were unprepared and cancelled.  Board meetings were held online to formalize the new reality, and practice sessions were held to familiarize members new to the technology and to work out procedures for conducting business and social meetings.  Meanwhile, the Timberland Regional Library developed a plan to offer public presentations on-line as well, which promises to bring back the artist showcase to Hypatia, but without the meet-and-greet potluck dinners: bring-your-own-fare with social distancing just hasn’t caught on as a satisfying social outlet.

By the time September arrived, everything was in place with most organizations.  So far, both weaving guilds have had very successful meetings, with nearly 40 members participating in each, of the 65 and 115 total members, respectively, and the first Hypatia resident artist on-line presentation is scheduled for next week.  The results have been most interesting.  No longer do we have to drive for 30 minutes to an hour to attend meetings.   The formalism imposed by the electronic medium ensures the business meetings move smoothly and keep within the schedule.  A major draw for the fiber arts groups is the sharing of work by members.  While we can’t examine the works in person, everyone has a front seat at the presentation, and many of us prefer to share previously taken photographs on-screen, which gives an added dimension, with closeups.  Committee reports are easily displayed on-screen rather than just read.  The tool includes a mechanism for voting on motions, with an exact count.  As one participant noted, we’re all face-to-face, where the in-person meetings take place with half the members looking at the backs of heads, and the rest craning backwards to see and hear a speaker at the back of the room.  It’s also easier to “mingle” before the meeting, since we don’t have to thread through the crowd to get to a person on the other side of the room.  There are still details and procedures to work out, but there are definite advantages along with the minor disadvantages to meeting in virtual space.

One of the other activities we’ve been missing out on is our yoga practice with the Senior Center.  A few weeks ago, the Senior Center started up scheduled Zoom sessions for some of the activities during this period when the center is closed to members.  Obviously, participating in a physical activity online is a bit different than sitting in the office with talking heads.  The first week we were able to participate, we set up in Judy’s craft room, which also serves as our TV room, with the big-screen monitor on which she watches her craft tutorials.  It’s not a big room.  For the second week’s session, our regular practice leader couldn’t be there, so I was tapped to lead the group (consisting of four of us–we’re hoping to build up to our usual 8 to 20 folks like we had at the Center over the last 10 years).  For this, I set up a computer in the sewing/quilting/weaving side of the fiber arts studios in the basement.  I use a setup with three cameras, so I had the “talking head” camera, one focused on the mat, and one for standing forms.  All went pretty well, except the ceiling, at a bit over 2 meters, didn’t permit full overhead stretches (I’m 186 cm), and, to get enough vertical field of view, my mat was too close to the bookcase to do side-twist arm and leg extensions from the mat.  Hopefully, our regular leader will be back next week: she has a bigger house with an empty room.

So, our real-time social world  is almost completely virtual.  In Episode 13 of the Quarantine Diaries, I described our breakdown of our aging network infrastructure.  Since we were spending money, I splurged on a docking adapter for my new laptop.  Today, for our on-line meeting, where both Judy and I had committee reports to present and a joint Show and Share slideshow of fiber projects we’ve been working on, I rearranged the workspace to configure three monitors on my laptop, extending the virtual world across the width of our work table.  We’re approaching the Internet Wall seen in the last century in such science fiction films like Fahrenheit 451 (1966), (Total Recall (1990), and Minority Report (2002).

In other areas, I’ve been spending more time mentoring on-line to computer students and budding software developers, most of whom appear to be in other than the United States, judging from the names, via questions posed on the Quora knowledge-sharing site.  We may be physically isolated in this era of closed borders and travel restrictions, but we still extend across the world, via the virtual environment.

Quarantine Diaries: Chapter 13 — The Machine Stops

When I was seven or eight, in 1951 or 1952, my father took me to an estate sale.  I’m not sure what he was looking for, probably tools, since he was starting his own refrigeration repair business. I, having never been to an auction before, didn’t know how it worked, but browsed around during the pre-auction inspection phase.  I found a book among the piled library boxes, apparently intended to be sold as a lot.  My father spoke to the auctioneer, and he agreed to sell me the book outside the lot, for a nominal sum of, I think, a nickel, no other bidders.

The first Sci-Fi anthology that carried the E.M.Forster 1909 classic “The Machine Stops.” I had a copy of this, but not sure I still do. It was my first “adult” book, bought at auction when I was 8, and the start of a life-long SciFi habit.

The book was The Science Fiction Galaxy, edited by Groff Conklin, paperback size, but with a hard cover, published in 1950 by PermaBooks, $0.35 cover price.  It was an anthology of science fiction short stories, and the first of many more I would collect through my teenage years and beyond.  I don’t know what fascinated me about the genre at the time, but it was an exciting concept.  Speculative fiction.  The first story in the book was a 1909 story by E.M. Forster, more famous for his novel A Passage to India. The story, The Machine Stops, was of a distant future, where people lived in isolation, each in their own room, maintained by The Machine, a universal automation system that provided all the needs, where the people communicated electronically.  Much like the current state of affairs during the Plague Years of the 21st century.  The quest for speed and excess had ruined much of the earth for human habitation with pollution.  Does this sound familiar?  We are living in Forster’s 1909 vision of The Machine in 2020.  I have never forgotten this dystopian paradise (that–spoiler alert–ends very badly), such an impact on my 7-year-old vision of what could be and might, if we gave ourselves over to technology instead of using it wisely, and how it could rob us of our humanity.  I kept getting reminders over the years of this classic story, which was the inspiration for George Lucas’ (Star Wars) debut 1971 film THX1138, and the original story concept for the 1967 novel Logan’s Run (later adapted to cinema)

The precocious reader, taking a lunch break on the original Parkins Refrigeration truck. The phone number (not visible) was 237. That’s it, 3 digits, no area code, no exchange prefix, no dials. Thinking about the future and The Machine, not dreaming it would come in his lifetime.

So, now it is 2020, and we are sustained by The Machine.  We hunker down in our houses, dependent (in many places, thankfully not ours) on air conditioning to keep the dwelling habitable in summer temperature highs that hover between 40 and 50 Celsius, sorely taxing the Machine that powers our civilization.  Because of the pandemic, food and supplies are delivered to our doorstep, lest we encounter crowds in stores or expose ourselves to contagion in public eateries.  We converse with colleagues, friends, and family in tiled windows on our computer screens, and even consult with our physicians via email and phone.

…Until The Machine Stops.

For several weeks now, we’ve encountered slowdowns and outages in our Internet service.  Sometimes the WiFi bogs down when our neighbors compete for radio frequency bandwidth, but this was different.  We had to reboot the router, and still had slow service.  A power surge one morning tripped one of the battery backup devices on our network, and it didn’t come back up. So, using The Machine (at least the Internet part of it), we ordered a new router and a new battery backup device, close to $400, more than double the last time we replaced these, eight or nine years ago.  Our income, of course, has not gone up in that time.  This is the last hurrah we can afford, to keep The Machine running for us.

The router arrived first.  It was, as our early 20th-century forebearers used to say, “Newfangled.”  We discovered that, in order to use any “modern” piece of technology, you can’t just turn it on and configure it with switches and keys on the panel, you have to use your phone, first downloading an application to talk to the new device, all of which requires you to have some sort of connection to the Internet through the telephone, which is now a complex computer in its own right.  People our age who continue to insist on using a plain old landline telephone or a simple hand-held mobile instrument that is just a device to talk to other people remotely, are unable to incorporate any other new technology into their lives, including replacing any vital pieces of tech that have broken down.  Our phones are getting old, too.  The Machine is a hungry master.

So, armed with the phone app, the new device was duly installed and fired up.  Since the simplified interface on the phone didn’t allow fine-tuning of the system, some of our other devices needed to be turned off so as not to interfere with the new device, which duplicated some of those functions in an as-yet to be determined manner, including turning them off to use our existing network tools.  Progress was slow.  Finally, it came time to hook up the non-wireless elements of the network.  But, when I did that, everything stopped.  The network was no longer connected to the Internet.  I tried putting the old router back in service, but it wouldn’t connect, either.  Slightly different symptoms, but no connection to the modem, which is a fancy name for the bridge circuit between the router and the cable company.  So, I used the Internet chat link on the cable company web page, with my phone, now switched to get Internet access through the phone company rather than the cable company.  Do we find it strange that you have to use the Internet to get help troubleshooting your Internet connection?  Yes.  One needs at least two different ways to talk to The Machine.

The chat, like most things these days, started with talking to a robot.  I’ve programmed artificial intelligence since the 1980s, when it was rather limited and “brittle,” as we call things that quickly reach the limits of their ability and can’t improvise.  It’s still brittle, the interface has just gotten a bit more conversational beyond the ELIZA chatterbot that’s been around since the 1960s, and the voice interfaces (about which, more later) have gotten a bit more natural inflection in speech output and the voice recognition and comprehension is comparable to an actual human assistant.  After the usual “Is the cord plugged in,” and “try rebooting the system” played out several times unsuccessfully, I got an actual human technician on the other end of the text chat.  We worked through a few things that robots aren’t allowed to do, to check to make sure the modem was operating.  It was, but the technician declared it was “too old” and needed to be replaced, anyway.

So, using the phone, I ordered a new modem online, finding out which store nearby had one in stock and would hold it for me.  We hopped in the car and drove 75 km to the store, masked up, collected the device at the front desk, and raced home.  Another $200 into this episode (the last one was $85).

The modem is the one part that needs to be blessed by the cable company, so back on-line we go with the Internet browser on the phone.  This time, it’s a web application that cheerfully fails to connect with the new modem after getting our account information.  It says, call the toll-free number–an anachronism from the days of long-distance calling, when phones were anchored to your house by wires to a mechanical relay bank downtown: the world is a local call now, with no extra tolls, just an exorbitant monthly fee.

The friendly-voiced robot on the other end gets all the information it needs, asks me to reset the modem (i.e., power it down, count to 10, plug it back in, etc.).  I do that, and call back, getting the same script (remember, limited options, brittle intelligence).  I know this won’t work, because what I need is to register a new modem, which requires talking to an actual human, something the robot hints at, but insists I try resetting the modem yet again.  I refuse, and then again refuse three times before the robot reluctantly, after chiding me for possibly wasting a human employee’s time, agrees to connect me with an actual human.  It then announces that there will be 30 minute wait, would I like a call back.  Yes, that’s much better than being on hold with distorted too-loud bad elevator music blaring in my ear for 40 minutes.

30 minutes later, the phone rings.  Another robot cheerfully asks if anyone is there?  I say yes, but this robot isn’t listening.  It’s waiting for a keypad tone, any key–I can’t find a key marked “Any,” so press ‘*’.  the robot didn’t hear it, decides I’m not responsive, says it will try later, and hangs up.  Fuming, I wait another 10 minutes.  This time, I immediately press ‘1′ which is magic: the robot accepts that as the correct “Any” key, verifies my identity, and connects me with a real person.  After a struggle, the modem gets programmed, but still won’t connect with the new router, or with the old router, either, though the light flickers dimly when I plug in the old one.  The nice person on the other end keeps on about “low voltage,” which I, as one schooled in these things, assume she means “low signal strength.”  Now I know that when you have a low WiFi signal, the WiFi adapter will connect but not actually let you login, so the obvious conclusion here is that the problem is on the cable side of the modem, which I don’t have the ability to check, other than stringing new cable to the other end of the house where the cable box is located.  But, she did ask me if the cables were tight, and I answered, “yes,” without actually checking other than with eyeballs.  Big mistake, as we shall see.  Meanwhile, we’re fixated on the “signal strength” theory.

So, the next step is an in-home visit from a technician, for which we set up an appointment.  So far, we’ve been “off the air” for nearly 12 hours, and it will be another 24 before the technician arrives.  The Machine has Stopped.  I’ve spent the day frantically beating a dead horse.  There’s no dinner, I’ve been on the phone, driven 150 km to the store and back, on the phone, and swapping hardware instead of cooking: Judy orders takeout, puts on her mask, and braves the virus to fetch it from across town.

It’s rather pleasant without Internet.  Oh, it hasn’t gone away entirely: we turned on cellular data for everything and kept using our phones, and I’m able to do some more setup internally in the network while we wait, though I’m using the old router (offline) so the machines can still talk to each other with their old addresses even if we’re not connected to the Internet.  I’ve converted some, but with dual networks so I can talk to them from either one.  But, we’ve downsized our cellular data plan since we don’t travel during the Plague, so we’re frugal with our data and skip some Zoom teleconferences to which we’d been invited, and I informed my client that I was offline but would be available for voice and text support over the phone.

To make a short story long, the technician arrived, not much later than promised, and a quick check revealed that the new router cable had become dislodged, but still attached, when I had plugged in the other network cables: the failure to connect the old router was because the old router had actually finally failed completely during this exercise.  Major embarrassment for the old codger who had been pulling other people’s fat out of the fire, fixing their computers and plugging in loose cables, for the past 55 years.  Oh, the ignominy of it all.  At least the new generation of techs is sharp enough to carry on.

The Machine is Up.  While this was going on, the new battery power supply arrived.  Everything got plugged in, and the network went wonky after the technician left.  This time, I figured it out.  I had disabled the address assignment service on my internal server, but forgotten to actually turn off the running instance, so it was handing out the wrong network addresses to our devices.  Sigh.  I’m not entirely happy with the way the routers handle this, since it doesn’t do its own internal name service to match machine names with addresses, so I will need to reconfigure the little network device to use the new addresses and old names and put it back on line, and disable the address service on the router, which I seem to be able to do from one of the browser menus, though it is obscure.

Meanwhile, the news creeps back in, of heat waves, unstoppable fires, horrific storms in tandem, unrest in the cities, government breaking with tradition and breaking down, propaganda building toward a contentious election, jobs still disappearing, the death rates and infection rates continue unabated, another polar ice shelf disappeared.  The Machine is grinding to a halt.  Look around, come out of your cell, take action.  Let us pull ourselves together, together, before it is too late.

Quarantine Diaries, Chapter 12 — The Night of the Bat

.The Plague rages on across the U.S., with no relief in sight.  People have apparently abandoned all social distancing and personal protection for the summer, and businesses have reopened.  We, on the other hand, have hunkered further down to avoid the crowds.  We continue our walks around town with masks at the ready, despite the heat, and still confine our shopping to once-a-week trip to the supermarket and another trip to the farm store.

Our first bicycle outing of the summer, at the airport industrial park, for a 10-km trial run.

Recovery continues: physical therapy has been downgraded to a biweekly schedule.  After I had several 20-25 minute sessions on the stationary bikeat home, we finally ventured out to the airport, our usual winter cycling venue: it’s relatively flat, and has a number of short loops so it’s possible to park the van in a central location and put in a 10-km ride without ever getting more than 1500 meters from the van.  Despite warnings to keep the first outings to 3-5 km, the rush was just too delicious, and we had a photo op to catch skydivers on final approach, so we pressed on to nearly 10 km.  Had a bit of sore hip afterwards, but a few home PT/yoga sessions and a 5-mile (8 km) walk around the north end of town, from home, worked out the kinks.

Judy enjoys photographing flower plantings in the yards around town, this one in the Mountain View neighborhood on the north side “alphabet streets”

Until early Wednesday morning, the day I was scheduled for PT.  Judy woke about 3:20 am: “Larye!  There’s something in the room.”

I was dozing, came awake to see a shadow flit across the ceiling.  I jumped out of bed and rushed around the end of the bed in the direction the shadow had gone.  Mistake.  A twinge, but not the searing sciatic jolt I had experienced in mid-June that put me down, unable to walk for weeks.  We turned on the lights.  Nothing.  Out in the hall.  Turned on the light. A winged shape fluttered down the hall and swerved around us, a leathery delta with pointed wingtip.  Not a bird.

“It’s a bat,” I said, as it disappeared behind me in the direction of the craft room.  I turned on the light there.  Nothing.  I checked the windows.  Our house is 90 years old, with the classic 6-over-1 bungalow sash windows.  In summer, we open the window and put an expanding screen in.  Usually, I put them in the top in this room, but Judy put them in while I was confined to the lower floor, and it was in the bottom.  This arrangement leaves a gap between the sashes about the same size as the opening to the bat house we have up in the eaves above the window.  That’s where the bat got in.  But, it obviously wasn’t going to find it to get out, so it was in the house somewhere.

We closed the doors to all the rooms to contain the beast.  When we closed the door to the craft room, Judy spotted the bat, perched near the lower hinge on the built-in storage cabinet behind the craft room door.  I pulled the screen off the nearest window,  grabbed a plastic container Judy keeps craft tools in, and emptied it.  Putting the open box over the bat, I slid the lid down to make it let go of the hinge.  It buzzed angrily, but let go, and was in the box with the lid closed.  I leaned out the window and dropped the bat onto the roof below, then replaced the screen.  Problem solved.  The bat lay on the roof, but when Judy went to check later, it was gone, so it hopefully didn’t come to harm, and we managed to handle it without danger of a bite or exposure to some zoonotic plague.  I rolled up a couple of hand towels and stuffed them in the gaps between the sashes in the two windows in which we had screens.

By SMBishop – Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=53580349

By now, it was barely past 3:30 am, but we were still adrenaline-charged, so went downstairs.  I stayed up, with an ice pack on my hip, but Judy went back to bed eventually.  The ice pack seemed to help.

Later that morning, we went through my PT exercises and in the afternoon, I went to the clinic for my session, where all went well.  But, the sudden movement earlier during the bat attack and the soreness after bicycling was a wake-up call that I’m not completely healed and still susceptible to further injury, something my primary physician warned me about.  So, dreams of quick recovery and taking some longer bicycle rides later in the fall are dashed.  We’ll have to be content with short rides.  It’s hard to ride from home, with the steep hills, so it’s a pain to have to drive somewhere to ride.  But, there’s the airport nearby, and maybe a trip to the county park where we based our spring rides, but do the shorter loop instead of the usual 15-25 km figure-8 route we usually take, and the relatively quiet loop around Island Lake north of town that we’ve also ridden before.