Sunday, May 31, 2020

World On Fire -- Bear Lake Day Trip

As a resident of Minneapolis sadly we're in a time where literally our city is on fire.  I considered skipping posting this week but rather decided to instead apply some of my FFmpeg knowledge to a number of day-trip media files and create a video of hiking in one of the most beautiful parks in the region.  Hoping to share some beauty in these trying times.

Took 139 pictures/videos, generated a number of video clips and panning crop filters on static images and stitched them all together using the following Makefile.

Enjoy.



$ cat -n Makefile 
     1 FPS=30
     2 JPGSECS=6
     3 Images=${wildcard */*.jpg} 
     4 ScaledImageClips=${filter-out %-scaled-scaled.mp4, ${subst .jpg,.mp4,${subst .jpg,-scaled.jpg,${Images}}}}
     5 Vids=${wildcard */*.mp4}
     6 ScaledVidClips=${filter-out %-scaled-scaled.mp4, ${subst .mp4,-scaled.mp4,${Vids}}}
     7
     8 all: final.mp4
     9
    10 final.mp4: video.mp4 audio.mp3
    11 # ${SH} ffmpeg -i video.mp4 -i audio.mp3 -acodec copy -shortest $@
    12 ${SH} ffmpeg -y -i video.mp4 -i audio.mp3 -af 'afade=t=out:st=193:d=3' -strict -2 -shortest $@
    13
    14 video.mp4: clip01.mp4 clip01b.mp4 clip02.mp4 clip03.mp4 clip04.mp4 clip05.mp4 clip06.mp4 clip07.mp4 clip07a.mp4 clip08.mp4 clip09.mp4 clip10.mp4 clip11.mp4 clip11a.mp4 clip12.mp4 clip13.mp4 clip13a.mp4 clip13b.mp4 clip14.mp4 clip15.mp4 clip16.mp4 clip17.mp4 clip18.mp4 clip19.mp4 clip19b.mp4 clip20.mp4 clip21.mp4 clip21a.mp4 clip21b.mp4 clip21c.mp4 clip22.mp4 clip23.mp4 clip24.mp4 clip25.mp4 clip26.mp4
    15 ${SH} rm concat.txt | true
    16 ${SH} for f in $^; do echo "file '$$f'" >> concat.txt; done
    17 ${SH} ffmpeg -y -f concat -i concat.txt -an $@
    18
    19 all2: ${ScaledImageClips} ${ScaledVidClips}
    20
    21 sortByTs:
    22 ${SH} echo "#!/usr/bin/python" > $@
    23 ${SH} echo 'import sys;' >> $@
    24 ${SH} echo 'import re;' >> $@
    25 ${SH} echo '' >> $@
    26 ${SH} echo 'inFile=sys.argv[1];' >> $@
    27 ${SH} echo '' >> $@
    28 ${SH} echo 'with open(inFile, "r") as fp:' >> $@
    29 ${SH} echo '  C=fp.read();' >> $@
    30 ${SH} echo '  L=C.split("\\n");' >> $@
    31 ${SH} echo '' >> $@
    32 ${SH} echo 'D=dict();' >> $@
    33 ${SH} echo 'for e in L:' >> $@
    34 ${SH} echo '  m=re.match(r"\D+(\d+_\d+)(\D+)",e);' >> $@
    35 ${SH} echo '  if m:' >> $@
    36 ${SH} echo '    D[m.group(1)]=e;' >> $@
    37 ${SH} echo '' >> $@
    38 ${SH} echo 'for k in sorted(D.keys()):' >> $@
    39 ${SH} echo '  print D[k];' >> $@
    40 ${SH} echo '' >> $@
    41 ${SH} chmod +x $@
    42
    43 storyboard.txt: files.list sortByTs 
    44 ${SH} ./sortByTs $< > $@
    45
    46 files.list:
    47 ${SH} for f in $(shell echo ${ScaledImageClips} ${ScaledVidClips} ); do echo $$f >> $@ ; done
    48
    49 storyboard.mp4: ${ScaledImageClips} ${ScaledVidClips} storyboard.txt
    50 ${SH} for f in `cat storyboard.txt`; do echo "file '$$f'" >> concat.txt; done
    51 ${SH} ffmpeg -y -f concat -i concat.txt -strict -2 $@
    52 ${SH} rm concat.txt
    53
    54 %-scaled.mp4: %.jpg 
    55 ${SH} mogrify -auto-orient $<
    56 ${SH} ffmpeg -y -loop 1 -i $< -f lavfi -i aevalsrc=0 -shortest -filter_complex "scale=1920:-2:force_original_aspect_ratio=decrease,scale=-2:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,drawtext=fontcolor=white:fontsize=36:fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf:text=$^" -r ${FPS} -vframes $(shell echo $(FPS)*$(JPGSECS) | bc) -strict -2 $@
    57
    58 %-scaled.mp4: %.mp4 
    59 ${SH} ffmpeg -y -i $< -filter_complex "scale=1920:-2,drawtext=fontcolor=white:fontsize=36:fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf:text=$^,scale=-2:1080,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" -r 30/1 -acodec copy $@
    60
    61 background.jpg: 
    62 ${SH} convert -size 1920x1080 xc:black $@
    63
    64 clip01.mp4: Motorola/IMG_20200515_091545370_HDR.jpg
    65 ${SH} ffmpeg -y -loop 1 -i $< -vf "crop=1920:1080:1000:1300-(5*n)" -r ${FPS} -vframes $(shell echo $(FPS)*$(JPGSECS) | bc) -vcodec libx264 $@
    66
    67 clip01b.mp4: Samsung/20200515_101726.mp4
    68 ${SH} ffmpeg -i $< -acodec copy -ss 2 -t 6 -vcodec libx264 $@
    69
    70 clip02.mp4: Motorola/IMG_20200515_100605080.jpg
    71 ${SH} ffmpeg -y -loop 1 -i $< -vf "crop=1920:1080:1000+(2*n):1300-(6*n)" -r ${FPS} -vframes $(shell echo $(FPS)*$(JPGSECS) | bc) -vcodec libx264 $@
    72
    73 clip03.mp4: Samsung/20200515_101151.mp4
    74 ${SH} ffmpeg -i $< -vf "crop=1920/2:1080/2:0:240,scale=1920:1080" -pix_fmt yuv420p -acodec copy -ss 21 -t 8 -vcodec libx264 $@
    75
    76 clip04.mp4: Samsung/20200515_101523.jpg
    77 ${SH} ffmpeg -y -loop 1 -i $< -vf "crop=1920:1080:1920+(5*n):1280-(2*n)" -r ${FPS} -vframes $(shell echo $(FPS)*$(JPGSECS) | bc) -vcodec libx264 $@
    78
    79 clip05.mp4: Samsung/20200515_102050.mp4
    80 ${SH} ffmpeg -i $< -vf "crop=1920:1080:0:0" -acodec copy -ss 11 -t 4 -vcodec libx264 $@
    81
    82 #--here
    83 clip06.mp4: Samsung/20200515_102948.mp4
    84 ${SH} ffmpeg -i $< -acodec copy -ss 5 -t 6 -vcodec libx264 $@
    85
    86 clip07.mp4: Motorola/VID_20200515_103223863.mp4
    87 ${SH} ffmpeg -i $< -acodec copy -ss 18 -t 3 -vcodec libx264 $@
    88
    89 clip07a.mp4: Samsung/20200515_104151.mp4
    90 ${SH} ffmpeg -i $< -acodec copy -ss 0 -t 6 -vcodec libx264 $@
    91
    92 clip08.mp4: Motorola/VID_20200515_104921374.mp4
    93 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 18 -t 3 -vcodec libx264 $@
    94
    95 clip09.mp4: Motorola/VID_20200515_104921374.mp4
    96 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 32 -t 6 -vcodec libx264 $@
    97
    98 clip10.mp4: Samsung/20200515_105549.mp4
    99 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 25 -t 6 -vcodec libx264 $@
   100
   101 clip11.mp4: Motorola/VID_20200515_105953712.mp4
   102 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 0 -t 6 -vcodec libx264 $@
   103
   104 clip11a.mp4: Motorola/VID_20200515_112402173.mp4
   105 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 6 -t 6 -vcodec libx264 $@
   106
   107 clip12.mp4: Samsung/20200515_113100.mp4
   108 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 19 -t 6 -vcodec libx264 $@
   109
   110 clip13.mp4: Motorola/VID_20200515_113612595.mp4
   111 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 0 -t 6 -vcodec libx264 $@
   112
   113 clip13a.mp4: Motorola/VID_20200515_123906857.mp4
   114 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 25 -t 6 -vcodec libx264 $@
   115
   116 clip13b.mp4: Motorola/IMG_20200515_124841138.jpg
   117 ${SH} ffmpeg -y -loop 1 -i $< -vf "crop=1920:1080:1920/2+(5*n):1280-(2*n)" -r ${FPS} -vframes $(shell echo $(FPS)*$(JPGSECS) | bc) -vcodec libx264 $@
   118
   119 clip14.mp4: Motorola/VID_20200515_114433396.mp4
   120 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 0 -t 10 -vcodec libx264 $@
   121
   122 clip15.mp4: Samsung/20200515_115303.mp4
   123 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 86 -t 6 -vcodec libx264 $@
   124
   125 clip16.mp4: Motorola/VID_20200515_121029775.mp4
   126 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 0 -t 4 -vcodec libx264 $@
   127
   128 clip17.mp4: Motorola/VID_20200515_121448017.mp4
   129 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 0 -t 2 -r 30 -vcodec libx264 $@
   130
   131 clip18.mp4: Motorola/VID_20200515_122255180.mp4
   132 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 14 -t 3 -vcodec libx264 $@
   133
   134 clip19.mp4: Motorola/VID_20200515_130750462.mp4
   135 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 0 -t 6 -vcodec libx264 $@
   136
   137 clip19b.mp4: Motorola/IMG_20200515_131917817.jpg
   138 ${SH} ffmpeg -y -loop 1 -i $< -vf "crop=1920:1080:1920/2+(5*n):960-(2*n)" -r ${FPS} -vframes $(shell echo $(FPS)*$(JPGSECS) | bc) -vcodec libx264 $@
   139
   140 clip20.mp4: Motorola/VID_20200515_132127514.mp4
   141 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 2 -t 3 -r 30 -vcodec libx264 $@
   142
   143 clip21.mp4: Samsung/20200515_133613.mp4
   144 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 9 -t 6 -vcodec libx264 $@
   145
   146 clip21a.mp4: Samsung/20200515_134350.mp4
   147 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 9 -t 6 -vcodec libx264 $@
   148
   149 clip21b.mp4: Motorola/VID_20200515_140305984.mp4
   150 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 0 -t 6 -vcodec libx264 $@
   151
   152 clip21c.mp4: Samsung/20200515_140926.mp4
   153 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 3 -t 6 -vcodec libx264 $@
   154
   155 clip22.mp4: Motorola/IMG_20200515_144241756_HDR.jpg
   156 ${SH} ffmpeg -y -loop 1 -i $< -vf "crop=1920:1080:1920/2+(5*n):1280-(2*n)" -r ${FPS} -vframes $(shell echo $(FPS)*$(JPGSECS) | bc) -vcodec libx264 $@
   157
   158 clip23.mp4: Samsung/20200515_145819.mp4
   159 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 28 -t 6 -vcodec libx264 $@
   160
   161 clip24.mp4: Samsung/20200515_145925.mp4
   162 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 7 -t 6 -vcodec libx264 $@
   163
   164 clip24a.mp4: Samsung/20200515_150020.mp4
   165 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 3 -t 6 -vcodec libx264 $@
   166
   167 clip25.mp4: Samsung/20200515_150020.mp4
   168 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 46 -t 6 -vcodec libx264 $@
   169
   170 clip26.mp4: Samsung/20200515_150837.mp4
   171 ${SH} ffmpeg -i $< -vcodec copy -acodec copy -ss 32 -t 6 -vcodec libx264 $@
   172
   173 audio.mp3: audio.mp4
   174 ${SH} ffmpeg -i $< -vn $@
   175
   176 audio.mp4: 
   177 # ${SH} youtube-dl https://www.youtube.com/watch?v=BLQaGEI5D2Q --format mp4 -o $@
   178 ${SH} youtube-dl https://www.youtube.com/watch?v=qSZGt_vQMcs --format mp4 -o $@
   179
   180 clean:
   181 ${RM} ${ScaledImageClips} ${ScaledVidClips}
   182 ${SH} svn revert $(shell svn status | grep "^M" | grep -v Makefile | cut -b 2-) | true
   183 ${RM} files.list files.txt


Sunday, May 24, 2020

Our 100th Blog Post


Armed with a 6th grade vocabulary and an appalling lack of proper use of grammar, I'll never consider myself a writer, but I do seem to enjoy writing at times.

My sister-in-law happened upon a clipping from my childhood local newspaper.  While I have absolutely no recollection of the event, right there in black-n-white apparently I had a taste for writing as a lad.  This 'young Grant', with his slim belt, bowl-cut hair and youthful interest in writing grew into a fat, middle-aged engineer.....with a bowl-cut haircut.  Somewhere along the way, his interest in writing withered and like most engineers he grew to dislike it.  Late in his 40's, this old lad repurposed his keyboard from it's purpose of coding and instead began writing about the one topic he truly, deeply and passionately loved....Computer Science.  

For years I was a 'bad contributor', a taker of knowledge, rarely contributing.  Routinely I'd encounter a question, or a compiler error, and hit up ye olde Stackoverflow to readily find a solution, a parasitic tick on the Internet knowledge vein.  Years of this going on, with the intent to one day add my contributions.

For many of those years I found myself learning something, not applying it immediately and learning it again when it became relevant to current work.  Like a good carpenter, software engineers acquire a surplus of general-purpose and specialized tools in our tool-shed and draw upon them when the proper time arrives.  Unfortunate for this alcohol-fueled engineer, there is limited quantity of retention in ye' olde brainpan, yet infinitely-available virtual space just waiting to store content.  So, one day I made the decision to document stuff as I learned it; that way, it was readily available for my own use when I was ready for it and it would be available for anyone else that was interested.  An on-line engineering workbook of sorts and a way to make a contribution back to the Internet community.

Early on, for months, perhaps a full year, my only motivation was writing for myself and making it available to anyone else that holds an interest.  Google-search would serve to provide an audience.  I didn't pay attention to traffic, simply writing what I want, when I wanted.  I would occasionally run out of content and not post for a few weeks, later only to restart as inspiration presented itself, but I always felt a sense of embarrassment when I failed to post.  Eventually, I became more dedicated to posting weekly without interruption and would spend considerable time authoring a queue of available content in the can, awaiting publishing.  My selected content was always: software-related, technical, a broad range of topics and 5-minute reads.  I'm inherently an impatient person, so writing for myself (or those like me) would have to fit that criteria.

In time I learned there is a great number of indirect benefits from writing.  Likely best described by the 'Feynman Technique', the best way to learn a topic is to focus your efforts in teaching it to someone.  Translate that to writing about it and you have a strong motivation for writing.  An article, report, paper or blog post should tell a story and while your university notebook may be comprised of clippings and snippets of information it lacks structure.  Writing an article, even if it is never read by anyone else, forces you to think through the topic and I guarantee you'll understand the material better after writing about it.  That's what I've found and a strong motivation for writing about things I'm interested in.

Along the journey however my motivations changed slightly, the more work you put into something the more you eventually want out of it.  In time, I began monitoring traffic to see if I was finding an audience, and sadly I found little evidence of one.  I began struggling to make the time to write something that would likely never be read.  I've struggled with that for the past few months, it's honestly difficult to consistently think of a topic, research it sufficiently, author some content and finally write about it.  My 'in the can' queue once in the dozen's have now dwittled to single-handed digits.  While there are countless topics available, to do them justice would require a significant time-investment, tough to invest the time knowing it would have little benefit/interest.  

I have one loyal reader; a champion of men, a leader of leaders, the guru of greatness; and to he I express the warmest of thanks (@MM).  

So, as of late I struggle with motivation.  I entered this journey writing for 'someone like me', thinking that if a topic is of interest to me there's gotta be others who find it interesting.  As the gas tank of inspiration runs dry I'm left with the overwhelming question of 'why am I spending time on this'?  I've dabbled in opinion-based topics, a good deal of FFMpeg stuff, and a spackling of various topics but struggled to find an audience. I've had some heavily viewed stuff, primarily driven by submitting posts to Hacker News;

There is always uncertainty with these statistics, an unknown quantity of the traffic is surely 'bots'.

In an effort to reclaim self-worth from this effort I'm gonna spend some time focusing on that promoting (e.g. marketing *shudder*).  In a world full of click-baiting, pleads for views, and evil techniques like spam it will be interesting to see how I can get more viewership while maintaining professional integrity and comfort zone.  

I'd like to express my thanks to those of you that made the time to read this.  I'd welcome any suggestions or recommendations you may have that would make this blog more useful and enjoyable.  Thank you, and stay safe.

Sunday, May 17, 2020

Python Generated Sequence Diagrams


Understanding a system or software architecture simply, truly, absolutely, undoubtedly requires an occasional sequence diagram.

I left university before object-oriented design became the norm, but it was trending up.  As part of my first job we were officially trained in OO and the use of Rational Rose.  The tool was integrated into our development process and environment, I embraced that tool, particularly in the ability to depict class interactions via 'message trace diagrams', otherwise known as 'sequence diagrams'.  Unfortunately, Rational Rose is pricey and uncommon in today's development practices but sequence diagrams are alive and well.  Whiteboarding diagrams is tedious and time-consuming, but the inter-webs has on-line tools that really ease the creation of diagrams; like this fella right here: https://www.websequencediagrams.com/

With a bit of effort, Python paired with ImageMagick can generate a rough approximation.

A quick tool/library can make this:
  diagram=Diagram();
  obj1=Object('object1');
  obj2=Object('object2');
  obj3=Object('object3');

  m1=Message(obj1,obj2,'method1(arg1)');
  diagram.add(m1);

  m2=Message(obj2,obj1,'return val');
  diagram.add(m2);

  m3=Message(obj1,obj1,'method2(abc)');
  diagram.add(m3);

  m4=Message(obj1,obj3,'method3()');
  diagram.add(m4);

  diagram.draw();

into this:


The code/library follows:
user@kaylee:~/PyMtd$ cat -n drawMtd 
     1 #!/usr/bin/python
     2 import os;
     3
     4 def log(S):
     5   print "__LOG '%s'"%(S);
     6   pass;
     7
     8 class Diagram: 
     9   def __init__(self):
    10     self.outFile_="./out.jpg";
    11     self.width_=0;
    12     self.height_=0;
    13     self.msgList_=[];
    14
    15   def add(self, msg):
    16     self.msgList_.append(msg);
    17
    18   def textDim(self, text):
    19     cmd="convert label:'%s' %s"%(text,'temp.jpg');
    20     os.system(cmd);
    21     cmd="identify temp.jpg | cut -f 3 -d ' '";
    22     retVal=os.popen(cmd).read();
    23     os.system("rm temp.jpg");
    24     return retVal.rstrip('\n');
    25
    26   def draw(self):
    27     betweenObj=100;
    28     objY=20;
    29
    30     #--position object, heads of lifelines
    31     iW=betweenObj;
    32     for msg in self.msgList_:
    33       msg.src_.y_=objY;
    34       msg.sink_.y_=objY;
    35       if msg.src_.x_==0:
    36         msg.src_.x_=iW;
    37         iW+=betweenObj;
    38       if msg.sink_.x_==0:
    39         msg.sink_.x_=iW;
    40         iW+=betweenObj;
    41
    42     #--draw message lines
    43     cmd="convert ";
    44     y=50;
    45     rightArrow="l -15,-5  +5,+5  -5,+5  +15,-5 z"
    46     leftArrow="l +15,+5  -5,-5  +5,-5  -15,+5 z"
    47     for msg in self.msgList_:
    48       src=msg.src_;
    49       sink=msg.sink_;
    50       if src.x_ == sink.x_:
    51         W=30;
    52         H=20;
    53         cmd+="-draw 'line %d, %d %d,%d' "%(src.x_,y, src.x_+W,y);
    54         cmd+="-draw 'line %d, %d %d,%d' "%(src.x_+W,y,src.x_+W,y+H);
    55         cmd+="-draw 'line %d, %d %d,%d' "%(src.x_+W,y+H,src.x_,y+H);
    56         cmd+="-draw \"path \'M %d,%d %s'\" "%(src.x_,y+H,leftArrow);
    57         D=self.textDim(msg.label_);
    58         tH=int(D.split('x')[0])/2;
    59         tW=int(D.split('x')[1])/2;
    60         cmd+="-draw 'text %d,%d \"%s\"' "%(src.x_+W+5,y+((tH)/2), msg.label_);
    61         y=y+H;
    62       else:
    63         cmd+= "-draw 'line %d,%d %d,%d' "%(src.x_,y,sink.x_,y);
    64         cmd+="-draw \"path \'M %d,%d %s'\" "%(sink.x_,y,(leftArrow if src.x_ > sink.x_ else rightArrow));
    65         D=self.textDim(msg.label_);
    66         textWidth=int(D.split('x')[0])/2;
    67         textHeight=int(D.split('x')[1])/2;
    68         cmd+="-draw 'text %d,%d \"%s\"' "%((src.x_+sink.x_)/2-textWidth, y-(textHeight/2), msg.label_);
    69       y+=20;
    70     self.height_=y+50;
    71     self.width_=iW;
    72     cmd+="-size %dx%d xc:white -fill none -stroke black "%(self.width_,self.height_);
    73
    74     #--draw lifeline
    75     L=[];
    76     for msg in self.msgList_:
    77       src=msg.src_;
    78       sink=msg.sink_;
    79       D=self.textDim(src.name_);
    80       w=int(D.split('x')[0])/2;
    81       for obj in [src, sink]:
    82         draw=not (obj in L);
    83         if (draw):
    84           cmd+="-draw 'text %d,%d \"%s\"' "%(obj.x_-w,obj.y_-5,obj.name_);
    85           cmd+="-draw 'stroke-dasharray 5 5 line %d, %d %d,%d' "%(obj.x_,obj.y_, obj.x_,self.height_-20);
    86           L.append(obj);
    87     
    88     cmd+="%s"%(self.outFile_);
    89     log(cmd);
    90     os.system(cmd);
    91
    92 class Object:
    93   def __init__(self,name):
    94     self.name_=name;
    95     self.x_=0;
    96     self.y_=0;
    97
    98 class Message:
    99   def __init__(self, srcObj, sinkObj,label):
   100     self.src_=srcObj;
   101     self.sink_=sinkObj;
   102     self.label_=label;
   103
   104
   105 def test00():
   106   diagram=Diagram();
   107   obj1=Object('object1');
   108   obj2=Object('object2');
   109   obj3=Object('object3');
   110
   111   m1=Message(obj1,obj2,'method1(arg1)');
   112   diagram.add(m1);
   113
   114   m2=Message(obj2,obj1,'return val');
   115   diagram.add(m2);
   116
   117   m3=Message(obj1,obj1,'method2(abc)');
   118   diagram.add(m3);
   119
   120   m4=Message(obj1,obj3,'method3()');
   121   diagram.add(m4);
   122
   123   diagram.draw();
   124
   125 def test01():
   126   diagram=Diagram();
   127   diagram.draw();
   128
   129 def test02():
   130   L=[];
   131   for i in range(0,10):
   132     L.append(Object('object%d'%i));
   133
   134   diagram=Diagram();
   135   for obj in L:
   136     m=Message(L[0],obj,'message x');
   137     diagram.add(m);
   138     m=Message(L[0],obj,'methodX()');
   139     diagram.add(m);
   140     diagram.add(Message(obj,obj,'ping'));
   141
   142   diagram.draw();
   143
   144 #---main---
   145 test00();
   146 #test01();
   147 #test02();

Pair this with some logging analysis, or debugger traces and you could auto-generate entire system interactions with ease.

Take it and do great things.

Sunday, May 10, 2020

Create ISO Image From Directory

Occasionally it's useful to create a CD/DVD ISO image from a list of files contained in a directory. For instance, if you're interested in archiving a bunch of files to be later burned to a CD/DVD.
This can be done by using the mkisofs command which stands for 'make ISO filesystem'.

If you have a list of directories like the following:
user@debian:/media/ehd/tmp$ ls -ltotal 2802468drwxr-xr-x 3 root root 4096 2009-12-29 13:01 disc01drwxr-xr-x 3 root root 4096 2009-12-29 13:01 disc02drwxr-xr-x 3 root root 4096 2009-12-29 13:02 disc03drwxr-xr-x 3 root root 4096 2009-12-29 13:03 disc04drwxr-xr-x 3 root root 4096 2009-12-29 13:05 disc05drwxr-xr-x 3 root root 4096 2009-12-29 13:06 disc06drwxr-xr-x 3 root root 4096 2009-12-29 13:08 disc07drwxr-xr-x 3 root root 4096 2009-12-29 13:09 disc08drwxr-xr-x 3 root root 4096 2009-12-29 13:10 disc09drwxr-xr-x 3 root root 4096 2009-12-29 13:10 disc10drwxr-xr-x 3 root root 4096 2009-12-29 13:12 disc11user@debian:/media/ehd/tmp$

You can create individual ISO images for each directory by issuing the following command:
$ mkisofs -o disc02.iso -J disc02

The ISO image will preserve the directory structures and file names found under disc02.

Cheers

Sunday, May 3, 2020

Don't Be In The Blind Spot

https://en.wikipedia.org/wiki/Vehicle_blind_spot
Whether you're cruising through traffic or in the office, it's always in your best interest to stay out of a blind spot -- Grant Lipelt, circa 2020.


I'm all for humility, midwestern nice, and a reserved nature of stating your achievements or participation in projects.  I child of 'Americans Heartland' we don't tend to make a 'big deal' of our contributions; work hard, keep your head down and do the work.  I've always been a "we" kinda guy; 'we fixed the problem', 'we discovered...',...it's a team effort.

Aside from annual performance appraisals, where you let your let your accomplishments fly, most people I work with aren't overly boastful.  Team members who are heavy on the "I", I don't tend to view favorably, even if they're being 100% honest.

But, when your boss is faced with making difficult decisions it's best that they have sufficient info to make it.  A good boss will know each members involvement in tasks, their proficiency, their experience and such.  Some don't though.  The risk to a heavy introvert who doesn't communicate their involvement in day-to-day activities or even their pure brilliance in solving significant problems 100% dependent on their boss paying close attention.

Focus on the work, do great stuff, and make sure your boss knows your value.  This isn't specific to remote work, but it certainly is heavily impacted.  Being the 'wizard behind the curtain', doing great things daily can sometimes be overlooked "out of sight, out of mind".  Make sure you're communicating your involvement, acknowledge the participation of others...it's a team effort.

Cheers.