プログラマーの仕事

こんにちは mtjです

プログラマーの仕事のメインはやはりプログラムを打つことがメインになると思います。
自分も他人に職業を説明する時は基本プログラマーと説明しています。

しかし実際にはプログラマーも範囲があり
本当にコードを打つ人、システムを作る人(メインはプログラミング)のような人もいるかと思います。

コードを打つ人であれば仕様書を理解して コーディングできる能力。
場合によっては軽く設計する力が必要かと思います。

システムを作る(設計する)人であればシステム周りの知識が必要でかなり広い範囲で知識が必要に感じます。

上記のような感じでシステムを作るにも段階的に知識が異なります
また上記のコードを打つ、設計、システムを考える等の境界はかなり曖昧だと思います。

コードに落とし込めるような仕様書を書くという話もどの程度書くのかでかなり所要時間に差が出ます。

但し、プログラマーを目指すならばシステム一連を作る知識も大事ですが 個人的には一日中PCの前で考えたり キーボード叩けるかが大事かと思ってます。
最終的にはどのような仕事も少しでも好きじゃないと成り立たないんじゃないかと思います。

膨らんだ小型バッテリーの処分

先月自室の掃除をしていたところ、机の奥から1つ前のiPhoneが発掘されたのですが、
何と画面が筐体から離れてぱかっと半開きの状態になっていました。
(画面ってこんな風に開くものなのだと驚きました)
原因はお察しの通り、バッテリーの膨張によるものでした。
Amazonで工具を買い自分で取り外してみたところ、
バッテリーがぶよぶよになって破れそうに思える状態になっており、
気付かず放置していたらと思うと怖いです。(掃除して良かった、、、!)

結局その膨らんだバッテリーは近所のゴミ処理場に持って行って回収してもらいました。
自分の住んでいる市の場合、膨らんでおらずリサイクルマーク(※)の付いているものなら、
市が役所やスーパーに設置している回収ボックスに入れるだけで良いみたいなのですが、
膨らんでいるものは直接持ち込む必要があり少々面倒でした。
(近場の電気屋にも電話してみたのですが断られました。。。)

自治体によっては、膨らんでいたら問答無用で回収不可のところもあるようなので、
回収してもらえただけありがたいです。

皆様も家に眠っているバッテリーがあればお気を付けください。。。


今回もう使わないモバイルバッテリーもついでに捨てようと調べた結果初めて知ったのですが、
リサイクルマークの有無も回収の際にとても重要となるようです。
ところがどうも、Amazonで売られている無名の海外メーカーのモバイルバッテリーの中には、
肝心のリサイクルバッテリーのないものが存在するようで、
そのハズレを捨てようと思うとどこにも回収してもらえずたらい回し状態になるとか。
怖いのでバッテリー内臓の製品は日本製ないし有名メーカーの物だけ買うようにします。。。

オムロンPLCと安川電機PLCとの通信

製造業の現場では、PLCを用いた設備制御が欠かせません。弊社では、工場設備と連携するシステム開発において、PLCとの通信、特にデバイスメモリの読み書き処理を数多く手がけています。

これまでの案件では、三菱電機製やキーエンス製のPLCとの通信が主流であり、これらに対応した社内ライブラリを整備済みです。これにより、設備との安定したデータ連携を実現し、品質管理や生産効率の向上に貢献してきました。

今回、新たな案件でオムロン製および安川電機製のPLCとの通信が必要となったことを受け、社内ライブラリの拡張を進めています。単なる個別対応ではなく、すべてのPLCに対して共通のインターフェイスでアクセス可能となり、保守性・拡張性の高いアーキテクチャを構築中です。

この取り組みにより、異なるメーカーのPLCを使用する現場でも、同一のコードベースで通信処理を実装できるようになります。例えば、製造ラインの一部を別メーカーのPLCに置き換える場合でも、システム側の変更は最小限に抑えられ、導入コストやリスクの低減が期待できます。

インフォテックは、工場の生産性改善、生産性向上に貢献致します。

流れ動作、作業の自動化

こんにちは mtjです。

作業の自動化という話は昔から依頼あるのですが それには様々な分析が必要になります。

まず人が判断している 作業にどのような判断が存在するかの分析が必要になります。
そしてそれをソフトに入れる事が可能かを判断します。
人がノリで右か左を押します のような案件はソフトでは難しいです。
ソフトでは最終的には0か1かが必要になります。

それを人が判断している場合は 判断している要件をまとめて センサーなりなんなりで0,1の判断できる状態まで落とし込む必要があります。
完全自動は上記をすべてデジタルに落とし込める段階になっていれば可能です。
判断が難しい箇所等は外部から人が操作する等で半自動化で進めます。

極端ではありますが1日一回人がボタンを押す半自動化か、その押す動作をどれだけの費用かけて完全自動化するか
という話が自動化、DX化には常に付きまといます。

基本は費用と時間があれば 根本的に不可能な物でない限り可能です。
現実は期間も予算もあるのでその中で可能な限り 効果のあるシステム、仕様を提案するのが自分たちの仕事になります。
何かの本でプログラマーには政治も大事との話がありますが 本当に大事だと感じます。

Anacondaのアップデートが進まなくなった場合の対処法

Pythonの仮想環境構築にはAnacondaを使用しています。
Pythonも新しいバージョンではswitch文が使用できたり型ヒントの機能が拡張されていたりと
色々便利になっていそうなので、Anacondaで使用できるバージョンを上げようと思いました。
しかしNavigator右上の更新ボタンから更新を実行しても、
‘updating package on root…’の状態から全く更新が進みません。

調べてみると、Anacondaを更新できないのはよくあることのようで、
中にはアンインストール&再インストールする人もいるようなのですが、
自分はGithubで見つけた方法[1]を、Navigatorは管理者権限で起動し
以下のように行うことで更新できました。
(管理者権限なしだとコマンドの実行時に更新失敗しました)

0. Navigatorを管理者権限で起動
1. Enviromentsのbase(root)でターミナルを開き、次のコマンドをそれぞれ実行

conda install anaconda=custom
conda update conda

2. Navigatorを管理者権限で再起動
3. 更新ボタンから更新実行

再インストールの場合は環境のバックアップが必要で面倒[2]なので、回避できて良かったです。
以上です。

[1] Navigator update to 2.1.0 from 2.0.4 never finishes · Issue #12643 · ContinuumIO/anaconda-issues · GitHub

[2] 環境のバックアップファイル内にはcondaのみでなくpipのパッケージも記載されるため、
pipを使用した環境であってもバックアップファイルを作成するだけでバックアップ可能であり、
かつ全環境の一括バックアップは公式では未サポートですが有志のスクリプトにより実行可能なので、
大変とまでは行かないのですが、とは言え成功したか確認が必要なこともあり手間がかかります。

他社設備のPCソフトを更新

検査設備などの他社作成PCソフトだけを更新したいというご要望がよくあります。
更新したい理由は色々で、最新のOSやPCに更新したい・機能追加をしたい・品質が低いので作り直したいなど。

大概の場合、元ソフトのソースコードが無いので、仕様書や電気図面などを参考にしたり、元ソフトを色々いじって目でコピーすることなります。

特に問題になるのが設備と独自の通信を行っている場合です。
一般的な通信インターフェイス(RS232C・TCP・GPIBなど)で設備と通信している場合は、通信を中継するソフトを作成して通信内容を調査すればなんとか対応可能です。
特殊なドライバ経由で通信を行っている場合は一番大変。途中のDLLを中継して調べられればラッキーですが、簡単にはいかないですね。
それでも色々な方法を駆使して調べますけど。

他のソフト会社は、このような案件はリスクが高いので引き受けてもくれなかったり、高額だったりするでしょうが、私はこのような案件が大好きですので、ご要望があればご連絡おまちしております。

自動化、省力化

こんにちは mtj です。

工場等では昔から進んでいる自動化、省力化 最近はレストランでも進んでいますね
普段の店で見るのであればセルフレジ、配膳ロボットでしょうか

猫ロボットは動いているとなかなかかわいかったです、普段自分たちが良く見るのはあれの下だけのパターンなので容姿があると印象が結構変わりました。
猫ロボットは配膳だけですけど行き来の回数を考えると効果はかなりの物になると思いました

やはり省力化はこういった細かい事から行う方が導入のハードルは低いんだなと感じました

工場等でこういった提案をするとよくあるのが 人のほうが安い、全部自動化しないと意味がない等言われることがあります。
全部自動化を猫ロボットで考えた場合 料理を自分で乗せて、自動的に判断して目的の場所へ移動、空の食器等は片付ける等でしょうか
面白さはありますが導入ハードルも開発期間もとてつもないでしょう

今後も労働人口が少なくなるに連れ省力化に取り組んでこなかった企業が淘汰される時代になると思います
プログラム開発もそうで 効率の良いIDE、ライブラリ等、テストの省力化等を使う能力が大事になってくると思っています
またシステムの提案の考え方も変わってくると思います

開発手法でもそういった方法を逃さず手に入れるようにしていきたいですね

C#でライフゲーム

今日はブログで書くネタがなかったので、C#でライフゲームを実装してみました。
コードは末尾に記載します。
※コンソールのカーソルを非表示にし忘れていました。コードでは修正済みです。

ライフゲームとは1970年に考案された
古典的かつシンプルな人工生命シミュレーションプログラムです。
ライフゲーム – Wikipedia
格子状に配置したセルに、生存と死亡の2つの状態を割り当て、各セルについて、
「現在のセルの状態及び周囲8セルの状態から次の状態を求めて、状態を更新する(※)」
を繰り返すことで、生命の生と死のようなサイクルをシミュレーションします。
※状態遷移は以下
・死亡かつ周囲生存3セル→生存(”誕生”)
・生存かつ周囲生存1セル以下→死亡(”過疎”)
・生存かつ周囲生存2セルor3セル→生存(”生存”)
・生存かつ周囲生存4セル以上→死亡(”過密”)

基本的にはどこかで変化が生じなくなりシミュレーションが止まるのですが、
中には変化をし続ける初期セルのパターンもあり、
現在でも愛好家により新しいパターンが発見されているようです。

近年の進化したAIに比べると随分とシンプルですが、単調過ぎず見ていて面白いですよね。
(若い頃に知って感動した故にそう感じるのかもしれませんが。。。)

10年ほど前、学部3年生の頃にゼミの課題で実装したことがありましたが、
(そのゼミは4年に上がる前に教授が他所に移ってしまったため消滅しました。。。)
当時はプログラミングを講義で習って1年ちょっとの段階だったのもあり、
動作はしたもののそれは酷いコードを書いていた覚えがあります。
(言語はほぼCの機能しか使用していない、なんちゃってC++で、描画はOpenGLで行っていました)

当時に比べると洗練されたプログラムを書けるようになったと思います。
継続は力なりということにして締めさせていただきます。

以上、老いを感じる、思い出語りを含んだ記事更新でした。

 // Program.cs
using LifeGame;

new Controller().Simulate(20, 0);
 // Lifegame.cs
using System.Diagnostics;
using System.Text;

namespace LifeGame;

/// <summary> モデルクラス </summary>
internal class Model {
    /// <summary> グリッド一辺サイズ </summary>
    public int Size { get; private set; } = 0;
    /// <summary> 現在の各セルの状態 </summary>
    public bool[,] Cells { get; private set; } = new bool[0, 0];
    /// <summary> 更新用のセル状態バッファ(兼以前の各セルの状態) </summary>
    private bool[,] _cells { get; set; } = new bool[0, 0];

    /// <summary> 初期化します。 </summary>
    public void Init(int size, int seed) {
        Size = size;
        Cells = new bool[Size, Size];
        _cells = new bool[Size, Size];
        var rnd = new Random(seed);
        for (var x = 0; x < Size; x++) {
            for (var y = 0; y < Size; y++) {
                // 乱数で初期状態設定
                Cells[x, y] = rnd.Next(0, 100) % 2 == 1;
            }
        }
    }

    /// <summary> 終端の状態か。 </summary>
    public bool IsTerminal() {
        for (var x = 0; x < Size; x++) {
            for (var y = 0; y < Size; y++) {
                var diff = Cells[x, y] != _cells[x, y];
                if (diff)
                    return false; // 差分あれば非終端
            }
        }
        return true; // 差分なしで終端
    }

    /// <summary> 更新します。 </summary>
    public void Update() {
        for (var x = 0; x < Size; x++) {
            for (var y = 0; y < Size; y++) {
                _cells[x, y] = GetNextState(x, y); // 次状態取得
            }
        }
        (Cells, _cells) = (_cells, Cells); // 更新した状態と現在の状態を交換
    }

    /// <summary> 次の状態を取得します。 </summary>
    private bool GetNextState(int x, int y) {
        var isAlive = Cells[x, y];
        var countAroundAlive = CountAroundAliveCells(x, y);
        return (isAlive, countAroundAlive) switch {
            (false, 3) => true, // 誕生
            (false, _) => false, // (死亡維持)
            (true, <= 1) => false, // 過疎
            (true, 2 or 3) => true, // 生存
            (true, >= 4) => false, // 過密
        };
    }

    /// <summary> グリッド走査用 x座標オフセット </summary>
    private static int[] Dx { get; } = { -1, 0, 1, -1, 1, -1, 0, 1 };
    /// <summary> グリッド走査用 y座標オフセット </summary>
    private static int[] Dy { get; } = { -1, -1, -1, 0, 0, 1, 1, 1 };

    /// <summary> 指定した中心セルの周囲の生存セルを数えます。 </summary>
    private int CountAroundAliveCells(int cx, int cy) {
        var count = 0;
        foreach (var (dx, dy) in Dx.Zip(Dy)) {
            // 中心座標+オフセットを正規化して座標算出
            var (x, y) = (ValidateIndex(cx + dx), ValidateIndex(cy + dy));
            if (Cells[x, y])
                count++; // 生存ならカウントアップ
        }
        return count;
    }

    /// <summary> セルのインデックスを正規化します。端と端が繋がるように。 </summary>
    private int ValidateIndex(int index) => index switch {
        < 0 => Size - 1, // 左の外側なら右端に
        _ when Size <= index => 0, // 右の外側なら左端に
        _ => index // 範囲内はそのまま
    };
}

/// <summary> ビュークラス </summary>
internal class View {

    /// <summary> 初期化します。 </summary>
    public void Init(Model model) {
        Console.Clear();
        Console.CursorVisible = false;
        Console.ForegroundColor = ConsoleColor.Green;
        Draw(model);
    }

    /// <summary> 終了します。 </summary>
    public void Term() {
        Console.ForegroundColor = ConsoleColor.Gray;
        Console.CursorVisible = true;
        Console.WriteLine("Done.");
    }

    /// <summary> 描画します。 </summary>
    public void Draw(Model model) {
        Console.SetCursorPosition(0, 0); // コンソール上書きのためカーソルを左上に
        var sb = new StringBuilder();
        for (var x = 0; x < model.Size; x++) {
            for (var y = 0; y < model.Size; y++) {
                var state = model.Cells[x, y] ? "■" : "_"; // 状態を文字化
                sb.Append(state);
            }
            sb.AppendLine();
        }
        Console.Write(sb.ToString()); // 出力
    }
}

/// <summary> 制御クラス </summary>
internal class Controller {
    /// <summary> モデル </summary>
    private Model Model { get; } = new();
    /// <summary> ビュー </summary>
    private View View { get; } = new();

    /// <summary> シミュレーションを実行します。 </summary>
    public void Simulate(int size, int seed) {
        var sw = new Stopwatch();
        Model.Init(size, seed);
        View.Init(Model);
        const int frameMs = 50; // 1フレーム50ms (20FPS)
        while (!Model.IsTerminal()) {
            // 更新所要時間を1フレーム時間から引いた上で待機
            Thread.Sleep(Math.Max(1, frameMs - (int)sw.ElapsedMilliseconds));
            sw.Restart();
            Model.Update(); // モデル更新
            View.Draw(Model); // 描画更新
        }
        View.Term();
    }
}

キーボード入力タイプのバーコードリーダ

工場向けの案件でキーボード入力タイプ(HID)のバーコードリーダを使用することがあります。

キーボードフックを使用してバーコード入力を取得するようにしていたのですが、取得したキーコードでは押されたキーが分かるだけなので、実際にはShiftキーやCtrlキーとのコンビネーションによって、キーコードから文字に変換する必要があります。
今までこの実装を適当に行っており、Shiftキーによる変換があまりできておらず、正しく読めない記号がありました。
今回、実装を見直しまして、ほぼ全てのパターンを網羅できたと思います。

以下がShiftキーを考慮した文字変換です。

return KeyCode switch {
    (int)Keys.Multiply => '*',
    (int)Keys.Separator => '|',
    (int)Keys.Add => '+',
    (int)Keys.Subtract => '-',
    (int)Keys.Decimal => '.',
    (int)Keys.Divide => '/',
    (int)Keys.Oemplus => shift ? '+' : ';',
    (int)Keys.Oemcomma => shift ? ' shift ? '=' : '-',
    (int)Keys.OemPeriod => shift ? '>' : '.',
    (int)Keys.Oem1 => shift ? '*' : ':',
    (int)Keys.Oem2 => shift ? '?' : '/',
    (int)Keys.Oem3 => shift ? '`' : '@',
    (int)Keys.Oem4 => shift ? '{' : '[',
    (int)Keys.Oem5 => shift ? '|' : '\\',
    (int)Keys.Oem6 => shift ? '}' : ']',
    (int)Keys.Oem7 => shift ? '~' : '^',
    (int)Keys.Oem102 => shift ? '_' : '\\',
    (int)Keys.NumPad0 => '0',
    (int)Keys.NumPad1 => '1',
    (int)Keys.NumPad2 => '2',
    (int)Keys.NumPad3 => '3',
    (int)Keys.NumPad4 => '4',
    (int)Keys.NumPad5 => '5',
    (int)Keys.NumPad6 => '6',
    (int)Keys.NumPad7 => '7',
    (int)Keys.NumPad8 => '8',
    (int)Keys.NumPad9 => '9',
    (int)Keys.D0 => '0',
    (int)Keys.D1 => shift ? '!' : '1',
    (int)Keys.D2 => shift ? '"' : '2',
    (int)Keys.D3 => shift ? '#' : '3',
    (int)Keys.D4 => shift ? '$' : '4',
    (int)Keys.D5 => shift ? '%' : '5',
    (int)Keys.D6 => shift ? '&' : '6',
    (int)Keys.D7 => shift ? '\'' : '7',
    (int)Keys.D8 => shift ? '(' : '8',
    (int)Keys.D9 => shift ? ')' : '9',
    >= (int)Keys.A and <= (int)Keys.Z => shift ? (char)KeyCode : (char)(KeyCode + 0x20),
    _ => char.ConvertFromUtf32(KeyCode)[0]
};

プログラムの違い

こんにちは mtj です。

プログラムと一概に言ってもそれを使用する場でかなりの意識の変化が必要になります。

  • 組み込み系
  • ネイティブのアプリ
  • WEBアプリ
  • サーバーサイドのプログラム

上記のそれぞれで考える事、注意する事がばらばらで同じ動作をさせようとしてもそれぞれで難易度が異なったりします。
また使用する言語、フレームワークの向き不向きもあるためかなり広い範囲での知識が必要になるように感じます

個別にどれをやっていたからどれが簡単等も少なく 基本はその分野について知っているかになると感じました
大まかなくくりの中にもどのネイティブ環境か フレームワークかCPUかだったり細かい違いがあるためさらに細かい分野知識も必要になりソフトが改めて奥が深いと感じます。

AI等も流行ってくるのでそれらも応用できるようになっていくのが時代の流れかもしれません。