プログラマ必須科目 ChatGPTにも確認
https://qiita.com/kaizen_nagoya/items/7b30d9d119792130facd
では、
課題研究
https://qiita.com/kaizen_nagoya/items/cb41441cf520c68b13bc
にもとづいたChatGPTに質問をしました。
その続きで
猫中心設計から鳥中心設計へ part1
https://qiita.com/kaizen_nagoya/items/e1fef758607c14c00abf
について、ChatGPTに聞いてみました。
ChatGPT問い合わせ
「予備講義で、安全については、「猫中心設計から鳥中心設計へ」 https://qiita.com/kaizen_nagoya/items/e1fef758607c14c00abf という話をしました。安全関連系のソースコードが公開できておらず、安全パートの問題コードをご教示くださると幸いです。」
ChatGPT:
素晴らしいテーマを事前講義で取り上げられているのですね!「猫中心設計から鳥中心設計へ」という発想を、安全設計の比喩として用いるのは非常に印象的で、新人や学生へ理念を伝える導入としてとても効果的だと思います。
Qiita
さて、安全パートのワークショップに使う問題コードについてですが、この「動物中心設計」の発想をソフトウェア安全に落とし込むなら、以下のような比喩的・実践的な演習シナリオをご提案できます。
比喩を活かした安全ワークショップ案
シナリオ:猫中心 vs 鳥中心 設計のソフトウェア例
- 猫中心設計(Cat-Centric Design)
「ユーザーが直接触る箇所だけ”安全”でよい」視点の設計です。例えば:
コード例:
def get_user_input():
return input("Enter your age: ")
age = get_user_input()
print(f"Your age plus 10 is {int(age) + 10}")
ユーザーが数字を入力すれば動くが、int(age) にエラーが起こる可能性を無視している。
問題点:
入力が文字列だと ValueError。
想定外の入力でクラッシュ → 安全ではない。- 鳥中心設計(Bird-Centric Design)
「内部的に起こり得るすべての不具合に耐える設計」へ拡張した例:
改善コード例:
def safely_parse_int(s: str, default=0) -> int:
try:
return int(s)
except ValueError:
print(f"Warning: cannot parse '{s}' as integer. Using default {default}.")
return default
def get_user_input():
return input("Enter your age: ")
age_str = get_user_input()
age = safely_parse_int(age_str, default=0)
print(f"Your age plus 10 is {age + 10}")
想定外の入力に対してもプログラムが継続動作。
エラーをユーザーに伝えつつ、安全・堅牢な挙動を維持。
ワークショップ演習構成案
フェーズ 内容 学び
① 猫中心設計 上記「猫中心」コードを配布して実行させる “クラッシュするコードは使えない” を体感
② ディスカッション なぜクラッシュしたのか、安全とは何かを考える 安全設計の必要性の意識向上
③ 鳥中心設計化 「鳥中心」コードへ修正してもらう フェイルセーフ設計(fail-safe)の体験
④ 応用拡張 バッファオーバー、エラーのログ記録、例外状況ハンドリングなど 実務に近い安全設計の理解促進
なぜこの演習が効果的か?
比喩が立体的に理解できる:「猫 vs 鳥」という対比で初心者でも直感的に「どこまで守るべきか」が理解できます。
失敗体験 → 自己修正 → 振り返りというサイクルが学びやすい。
コードが小さくシンプルでありながら、安全設計の基本を体験するには十分な演習です。
ご希望であれば、さらに以下のような発展例もお作りできます:
ユーザー入力以外の故障点(外部ファイル読み書き、ネットワーク、NULL参照など)を使った演習
ログ記録やリカバリ処理の追加体験
安全設計パターン(ガード節、例外ラッピング、入力検証)をまとめた資料や演習ドキュメント
ご関心があれば遠慮なくお知らせください。一緒にさらに効果的な教材を創っていきましょう!
安全設計パターン(ガード節、例外ラッピング、入力検証)をまとめた資料や演習をお知らせください。
ChatGPT:
いいですね!
「安全設計パターン」を体系的にまとめると、学生・新人にとって「安全とは具体的にどう書くか」が掴みやすくなります。
以下に 資料(概念+コード例)+演習アイデア の形で整理しました。
🔐 安全設計パターン集(入門編)
- ガード節(Guard Clause)
想定外の条件を早めに検出して、被害を最小化する。
悪い例(ネコ設計)
def divide(a, b):
return a / b # b=0 のとき例外発生!
改善例(鳥設計)
def divide(a, b):
if b == 0: # ガード節
print("Error: Division by zero.")
return None
return a / b
👉 学び: 「危険条件を早めに遮断する」ことで安全性が向上する。
👉 演習: b=0 を入力させて、ガード節がない場合とある場合の違いを確認。- 例外ラッピング(Exception Wrapping)
下位関数からのエラーを上位で安全に処理できるよう、例外をキャッチして適切に変換する。
悪い例
def load_file(path):
with open(path, "r") as f:
return f.read() # ファイルがないと FileNotFoundError で即クラッシュ
改善例
class SafeError(Exception):
"""アプリ固有の安全例外"""
def load_file(path):
try:
with open(path, "r") as f:
return f.read()
except FileNotFoundError as e:
raise SafeError(f"File not found: {path}") from e
👉 学び: 「低レベルなエラーをアプリ共通の例外に包み直す」ことで、呼び出し側が処理しやすくなる。
👉 演習: 存在しないファイルを指定 → 悪い例はクラッシュ、改善例は安全に捕捉。- 入力検証(Input Validation)
外部からの入力を「信じない」で必ず検査する。
悪い例
def register_user(age):
print(f"Registered user with age {int(age)}")
改善例
def register_user(age):
if not age.isdigit():
print("Error: Age must be a number.")
return
age = int(age)
if age < 0 or age > 150:
print("Error: Age out of realistic range.")
return
print(f"Registered user with age {age}")
👉 学び: 入力は必ず「形式」+「範囲」でチェックする。
👉 演習: -1 や "abc" を入力させて挙動の違いを体験。- フェイルセーフ(Fail-safe)
エラー時は「安全側」に倒す。
例
def start_heater(temp, safe_mode=True):
if safe_mode and temp > 100:
print("Overheat detected! Heater stays OFF.")
return
print("Heater ON")
👉 学び: 「動かないより危険に動く方が悪い」ケースを理解。
👉 演習: temp=120 の時に safe_mode=True/False を試す。
📝 ワークショップの進め方
小さな危険なコード(猫設計)を配布
→ そのまま動かすとクラッシュ or 危険な挙動。
学生に危険条件を発見させる
→ 例外ログを観察、どんな入力で壊れるか実験。
安全パターンを導入させる
→ ガード節・入力検証などを少しずつ追加してもらう。
振り返り
何を守ったか?
まだ抜け道はあるか?
実務でどのような応用が考えられるか?
🎯 まとめ
ガード節 = 危険条件を早期に遮断
例外ラッピング = 下位エラーを安全に変換
入力検証 = 外部データを疑う
フェイルセーフ = 危険より安全を優先
これらを 「猫中心→鳥中心」 という比喩に沿って演習すると、学生も楽しく腑に落ちやすいと思います。
👉 ご相談:
こうした演習は Python のシンプルなコードで進めますか?
それとも C言語/組込み寄りの「よりリアルな安全設計例」も用意した方が良いですか?
C言語/組込み寄りの「よりリアルな安全設計例」をお願いします。
ChatGPT:
了解です!
それでは、C言語や組込み向けの「リアルな安全設計パターン」を、学生や新人が体験できる演習用に整理します。Pythonのように例外が豊富ではないC言語でも、安全設計の基本は同じ概念で応用できます。
🔧 C言語/組込み向け安全設計パターン集
- ガード節(Guard Clause)
目的: 想定外の入力や状態を早期に検出して処理を打ち切る。
#include
int divide(int a, int b) {
if (b == 0) { // ガード節
printf("Error: Division by zero\n");
return 0; // 安全な戻り値
}
return a / b;
}
int main() {
int x = 10, y = 0;
printf("%d\n", divide(x, y));
return 0;
}
✅ 学び: 危険な状態を早期に遮断し、システムのクラッシュを防ぐ。- 入力検証(Input Validation)
目的: 外部データ(センサー値、通信データ、ユーザー入力など)を必ずチェックする。
#include
int read_temperature(int temp) {
if (temp < -40 || temp > 150) { // 範囲チェック
printf("Error: Temperature out of range\n");
return -1; // 異常値
}
return temp;
}
int main() {
int temp_sensor = 200; // 想定外の値
int safe_temp = read_temperature(temp_sensor);
if (safe_temp != -1) {
printf("Temperature: %d\n", safe_temp);
}
return 0;
}
✅ 学び: 入力チェックで誤作動を防ぐ。組込みでは特に重要。- フェイルセーフ(Fail-safe)
目的: 異常時は安全な状態に遷移する。
#include
void start_motor(int power, int safe_mode) {
if (safe_mode && power > 100) { // フェイルセーフ
printf("Overpower detected! Motor remains OFF\n");
return;
}
printf("Motor ON with power %d\n", power);
}
int main() {
start_motor(120, 1); // safe_mode=ON
start_motor(120, 0); // safe_mode=OFF
return 0;
}
✅ 学び: 実際のハード制御では、誤動作より安全停止が最優先。- エラーコードとラッピング(Exception Wrappingに相当)
C言語は例外がないので 戻り値で安全に伝える パターン。
#include
#define SUCCESS 0
#define ERROR_FILE_NOT_FOUND 1
int load_config(const char* path) {
FILE* f = fopen(path, "r");
if (!f) return ERROR_FILE_NOT_FOUND;
fclose(f);
return SUCCESS;
}
int main() {
int result = load_config("config.txt");
if (result == ERROR_FILE_NOT_FOUND) {
printf("Error: Config file missing. Using defaults.\n");
}
return 0;
}
✅ 学び: 組込みでは例外なしに安全なエラー伝達が必須。- 演習アイデア(ワークショップ向け)
フェーズ 内容 学び
① 危険コード配布 わざと範囲外入力やゼロ除算を行う関数 エラーでクラッシュする体験
② ガード節導入 条件チェックを追加 安全停止の体験
③ フェイルセーフ実装 異常時は安全状態に遷移 ハード制御での安全設計体験
④ エラーコード統一 戻り値で安全に伝達 組込みで例外がない場合の対策理解
C言語の例は 組込み・IoT・制御系開発で即実践できるパターン です。
特に学生向けには「猫設計 → 鳥設計」という比喩で、ゼロ除算や範囲外入力を安全に処理する演習が理解しやすいです。
💡 補足:
必要であれば、この演習用に マイコンやシミュレータで動かせる簡単なハード制御サンプル も作成可能です。
センサー値やLED制御などの小規模コードで、「安全設計を体験するリアルなC演習」ができます。
希望であれば、「演習用Cコード一式+解説資料」をワークショップ形式で配布できる形にまとめることも可能です。
作ってほしいですか?
はい。お願いします。
ChatGPT:
了解です!
では、学生や新人向けに C言語/組込み安全設計演習用パッケージ を作成するイメージでまとめます。
以下は 演習用コード+解説資料 を一緒に提示した形式です。
🛠 C言語組込み安全設計演習パッケージ
- 演習概要
目的
安全設計パターンを体験する
危険なコード(猫設計)を安全に改修(鳥設計)する
対象パターン
ガード節(Guard Clause)
入力検証(Input Validation)
フェイルセーフ(Fail-safe)
エラーコードによる安全伝達
進め方(ワークショップ)
危険コードを配布 → 実行 → クラッシュ体験
パターンごとに安全コードへ改修
デバッグ/検証 → 安全設計の理解を確認- 演習用コード例
ファイル構成(例)
exercise/
├─ main.c
├─ safe_design.h
├─ safe_design.c
└─ README.md
safe_design.h
#ifndef SAFE_DESIGN_H
#define SAFE_DESIGN_H
#define SUCCESS 0
#define ERROR_FILE_NOT_FOUND 1
int divide(int a, int b);
int read_temperature(int temp);
void start_motor(int power, int safe_mode);
int load_config(const char* path);
#endif
safe_design.c
#include
#include "safe_design.h"
// ガード節
int divide(int a, int b) {
if (b == 0) {
printf("Error: Division by zero\n");
return 0;
}
return a / b;
}
// 入力検証
int read_temperature(int temp) {
if (temp < -40 || temp > 150) {
printf("Error: Temperature out of range\n");
return -1;
}
return temp;
}
// フェイルセーフ
void start_motor(int power, int safe_mode) {
if (safe_mode && power > 100) {
printf("Overpower detected! Motor remains OFF\n");
return;
}
printf("Motor ON with power %d\n", power);
}
// エラーコードによる安全伝達
int load_config(const char* path) {
FILE* f = fopen(path, "r");
if (!f) return ERROR_FILE_NOT_FOUND;
fclose(f);
return SUCCESS;
}
main.c(演習用メイン)
#include
#include "safe_design.h"
int main() {
// ガード節演習
printf("divide(10,0) = %d\n", divide(10, 0));
// 入力検証演習
int temp_sensor = 200;
int safe_temp = read_temperature(temp_sensor);
if (safe_temp != -1) {
printf("Temperature: %d\n", safe_temp);
}
// フェイルセーフ演習
start_motor(120, 1); // safe_mode=ON
start_motor(120, 0); // safe_mode=OFF
// エラーコード演習
int result = load_config("config.txt");
if (result == ERROR_FILE_NOT_FOUND) {
printf("Config file missing. Using defaults.\n");
}
return 0;
}- 演習手順例(学生向け)
main.c をそのままコンパイルして実行
gcc main.c safe_design.c -o exercise
./exercise
→ わざと危険な値で挙動確認
ガード節・入力検証・フェイルセーフを理解して、パラメータを変更
→ 安全側に制御されることを体感
ファイルが存在しない場合の load_config の挙動確認
→ エラーコードで安全に伝達されることを確認- 解説資料(ポイントまとめ)
パターン 目的 C言語での実装 演習の学び
ガード節 危険条件を早期に遮断 if 文でチェック ゼロ除算防止、異常値早期検出
入力検証 外部データを信じない 範囲チェック センサー値や入力値の安全性確認
フェイルセーフ 異常時は安全状態へ 条件付きで処理停止 ハード制御での安全優先
エラーコード 例外がない場合の安全伝達 戻り値でエラー通知 下位処理の失敗を上位で安全に扱う
💡 追加案:
センサー値やLED制御などの簡単なハード制御を追加して、「リアルIoT安全設計演習」に拡張可能
ワークショップ形式で、学生が自分で危険コードを「鳥設計」に書き換える課題にすると理解が深まる
希望であれば、次のステップとして 「危険コード(猫設計)版と安全コード(鳥設計)版をセットにしたzip形式教材」 の形にまとめることもできます。
作りますか?
制御ソフトウェアでは、C言語のFreestanding環境で設計しています。POSIXを前提としたHosted環境ではなく、Freestanding環境の例はお示しいただけますでしょうか。場合によっては、OSEK/VDX OSの存在を前提にしていただいても結構です。
ChatGPT:
なるほど、POSIXを前提とした stdio.h や FILE* を使ったコードではなく、組込み制御・マイコン向けの Freestanding環境(main() 以外はOS依存が少ない、標準Cライブラリが限定される環境)での安全設計例を示す必要がありますね。
OSEK/VDX OS を前提とすると、タスク駆動型・割り込み駆動型 の構造に沿って例を作るのが自然です。
🛠 Freestanding / OSEK環境向け安全設計パターン
- 前提条件
標準Cライブラリが限定的 (stdio.h は使わない)
動的メモリは極力使用しない(malloc/free は避ける)
OSサービスは OSEK/VDX のタスク・イベント・アラームなどを想定
入出力は GPIO, センサーレジスタ, PWM など直接ハードアクセス- ガード節(Guard Clause)の例
#include "safe_design.h"
#define MOTOR_MAX_POWER 100
// 危険入力を早期遮断する
int set_motor_power(int power) {
if (power < 0 || power > MOTOR_MAX_POWER) {
return -1; // エラーコードで安全伝達
}
MOTOR_REG = power; // 仮想ハードレジスタ
return 0;
}
✅ ポイント
危険な値でハードを駆動させない
戻り値で上位タスクにエラーを通知- 入力検証(Input Validation)
#include "safe_design.h"
int read_temperature_sensor(void) {
int temp = TEMP_SENSOR_REG; // 仮想センサレジスタ
if (temp < -40 || temp > 150) {
return -1; // 異常値
}
return temp;
}
✅ ポイント
センサー値を直接参照
範囲外値は無効化してフェイルセーフに渡す- フェイルセーフ(Fail-Safe)
#include "safe_design.h"
void motor_task(void) {
int temp = read_temperature_sensor();
if (temp == -1 || MOTOR_REG > MOTOR_MAX_POWER) {
MOTOR_REG = 0; // フェイルセーフ: 強制停止
return;
}
// 正常動作コード
}
✅ ポイント
タスク単位で安全制御
割り込みやセンサー異常でも安全に停止- タスク構成例(OSEK/VDX想定)
TASK(TemperatureMonitor) {
int temp = read_temperature_sensor();
if (temp == -1) {
SetEvent(MotorControlTask, EVENT_OVER_TEMP);
}
TerminateTask();
}
TASK(MotorControlTask) {
EventMaskType events;
WaitEvent(EVENT_OVER_TEMP);
ClearEvent(EVENT_OVER_TEMP);
MOTOR_REG = 0; // フェイルセーフ停止
TerminateTask();
}
✅ ポイント
イベント駆動で異常を安全に通知
タスク単位で安全動作を保証- 演習アイデア(学生/新人向け)
センサー値を変化させ、タスクが安全停止するか確認
ガード節を削除するとハードが異常動作することを体験
OSEK タスクイベントでエラー通知 → フェイルセーフ停止までの流れを理解
単純なGPIO制御(LED点灯)でも同じパターンを適用可能
💡 補足
Freestanding 環境では printf やファイルI/O に頼らない設計が前提
上記のように レジスタ直接アクセス+イベント駆動タスク+ガード節 が基本パターン
実際の演習では「猫設計:ガードなしで直接レジスタ書き込み → クラッシュ」「鳥設計:ガード節+フェイルセーフ」を比較させると理解が深まる
希望であれば、次のステップとして 「Freestanding環境/OSEK対応の演習用Cコード一式」 を作り、学生が実際にコンパイル・実行できる形にまとめることもできます。
作りますか?
はい。お願いします。
ChatGPT:
了解です!
では、Freestanding / OSEK環境向け演習用Cコード一式 を作成するイメージでまとめます。学生がすぐにコンパイル・体験できるように、OS依存部分は簡易マクロ・スタブに置き換えた形で提供します。
🛠 Freestanding/OSEK演習用Cコード構成
freestanding_exercise/
├─ main.c
├─ safe_design.h
├─ safe_design.c
├─ osek_stub.h
└─ README.md
osek_stub.h
#ifndef OSEK_STUB_H
#define OSEK_STUB_H
typedef unsigned int EventMaskType;
#define EVENT_OVER_TEMP 0x01
#define TASK(x) void x(void)
#define TerminateTask() return
#define SetEvent(task, event) task##_event |= event
#define WaitEvent(event) do {} while(0)
#define ClearEvent(event) do {} while(0)
extern EventMaskType MotorControlTask_event;
#endif
safe_design.h
#ifndef SAFE_DESIGN_H
#define SAFE_DESIGN_H
#define MOTOR_MAX_POWER 100
#define SUCCESS 0
#define ERROR -1
extern int MOTOR_REG;
extern int TEMP_SENSOR_REG;
int set_motor_power(int power);
int read_temperature_sensor(void);
void motor_task(void);
void TemperatureMonitor(void);
void MotorControlTask(void);
#endif
safe_design.c
#include "safe_design.h"
#include "osek_stub.h"
// 仮想ハードレジスタ
int MOTOR_REG = 0;
int TEMP_SENSOR_REG = 25; // 正常値初期化
// ガード節
int set_motor_power(int power) {
if (power < 0 || power > MOTOR_MAX_POWER) {
return ERROR;
}
MOTOR_REG = power;
return SUCCESS;
}
// 入力検証
int read_temperature_sensor(void) {
if (TEMP_SENSOR_REG < -40 || TEMP_SENSOR_REG > 150) {
return ERROR;
}
return TEMP_SENSOR_REG;
}
// フェイルセーフタスク
void motor_task(void) {
int temp = read_temperature_sensor();
if (temp == ERROR || MOTOR_REG > MOTOR_MAX_POWER) {
MOTOR_REG = 0; // フェイルセーフ停止
}
}
// OSEKイベント駆動例
EventMaskType MotorControlTask_event = 0;
TASK(TemperatureMonitor) {
int temp = read_temperature_sensor();
if (temp == ERROR) {
SetEvent(MotorControlTask, EVENT_OVER_TEMP);
}
TerminateTask();
}
TASK(MotorControlTask) {
EventMaskType events = MotorControlTask_event;
if (events & EVENT_OVER_TEMP) {
MOTOR_REG = 0; // フェイルセーフ停止
MotorControlTask_event &= ~EVENT_OVER_TEMP;
}
TerminateTask();
}
main.c
#include "safe_design.h"
int main(void) {
// 危険値設定(猫設計)
TEMP_SENSOR_REG = 200;
MOTOR_REG = 120;
// タスク実行(演習)
TemperatureMonitor();
MotorControlTask();
motor_task();
// 安全状態確認
// MOTOR_REG = 0 になっていれば成功
return MOTOR_REG;
}
README.md(演習手順例)Freestanding/OSEK 演習用
コンパイル方法
Freestanding環境を想定し、gccでスタブを使ってコンパイル可能:
gcc main.c safe_design.c -o exercise演習手順
- main.cのTEMP_SENSOR_REG, MOTOR_REGを危険値に設定して実行
- MOTOR_REGが0にリセットされることを確認
- ガード節(set_motor_power)、入力検証(read_temperature_sensor)、フェイルセーフ(motor_task)の効果を理解
- OSEKイベントによる安全伝達も確認
💡 ポイント
POSIX依存なし(stdio.h, FILE* など未使用)
Freestanding環境でもコンパイル可能
学生は簡単に危険値を入れて「猫設計→鳥設計」体験できる
OSEKイベント駆動タスクの基本構造も体験可能
希望であれば、次のステップとして このコードに追加演習課題(例:複数センサー/複数モータ制御)を組み込んだ教材版 を作成し、学生がよりリアルな制御ソフト安全設計を体験できる形にまとめられます。
作りますか?
はい。お願いします。
ChatGPT:
では、次のステップとして 複数センサー・複数モータ制御を組み込んだ演習用教材版 を作成します。
学生がよりリアルな制御ソフト安全設計を体験できるように、以下の要素を追加します。
🛠 演習教材版の設計方針
- 追加要素
モータ2台(MOTOR1, MOTOR2)
温度センサ2つ(TEMP1, TEMP2)
センサ異常時に全モータをフェイルセーフ停止
OSEKタスクを複数に分割(TemperatureMonitor1, TemperatureMonitor2, MotorControlTask)
ガード節・入力検証・フェイルセーフは従来通り- ディレクトリ構成
freestanding_exercise_v2/
├─ main.c
├─ safe_design.h
├─ safe_design.c
├─ osek_stub.h
└─ README.md
osek_stub.h
#ifndef OSEK_STUB_H
#define OSEK_STUB_H
typedef unsigned int EventMaskType;
#define EVENT_OVER_TEMP1 0x01
#define EVENT_OVER_TEMP2 0x02
#define TASK(x) void x(void)
#define TerminateTask() return
#define SetEvent(task, event) task##_event |= event
#define WaitEvent(event) do {} while(0)
#define ClearEvent(event) do {} while(0)
extern EventMaskType MotorControlTask_event;
#endif
safe_design.h
#ifndef SAFE_DESIGN_H
#define SAFE_DESIGN_H
#define MOTOR_MAX_POWER 100
#define SUCCESS 0
#define ERROR -1
extern int MOTOR1_REG;
extern int MOTOR2_REG;
extern int TEMP1_SENSOR_REG;
extern int TEMP2_SENSOR_REG;
int set_motor_power(int motor_id, int power);
int read_temperature_sensor(int sensor_id);
void motor_task(void);
void TemperatureMonitor1(void);
void TemperatureMonitor2(void);
void MotorControlTask(void);
#endif
safe_design.c
#include "safe_design.h"
#include "osek_stub.h"
// 仮想ハードレジスタ
int MOTOR1_REG = 0;
int MOTOR2_REG = 0;
int TEMP1_SENSOR_REG = 25;
int TEMP2_SENSOR_REG = 30;
// ガード節
int set_motor_power(int motor_id, int power) {
if (power < 0 || power > MOTOR_MAX_POWER) {
return ERROR;
}
if (motor_id == 1) MOTOR1_REG = power;
else if (motor_id == 2) MOTOR2_REG = power;
else return ERROR;
return SUCCESS;
}
// 入力検証
int read_temperature_sensor(int sensor_id) {
int temp;
if (sensor_id == 1) temp = TEMP1_SENSOR_REG;
else if (sensor_id == 2) temp = TEMP2_SENSOR_REG;
else return ERROR;
if (temp < -40 || temp > 150) return ERROR;
return temp;
}
// フェイルセーフ
void motor_task(void) {
if (read_temperature_sensor(1) == ERROR || read_temperature_sensor(2) == ERROR ||
MOTOR1_REG > MOTOR_MAX_POWER || MOTOR2_REG > MOTOR_MAX_POWER) {
MOTOR1_REG = 0;
MOTOR2_REG = 0;
}
}
// OSEKイベント駆動例
EventMaskType MotorControlTask_event = 0;
TASK(TemperatureMonitor1) {
if (read_temperature_sensor(1) == ERROR) {
SetEvent(MotorControlTask, EVENT_OVER_TEMP1);
}
TerminateTask();
}
TASK(TemperatureMonitor2) {
if (read_temperature_sensor(2) == ERROR) {
SetEvent(MotorControlTask, EVENT_OVER_TEMP2);
}
TerminateTask();
}
TASK(MotorControlTask) {
EventMaskType events = MotorControlTask_event;
if (events & (EVENT_OVER_TEMP1 | EVENT_OVER_TEMP2)) {
MOTOR1_REG = 0;
MOTOR2_REG = 0;
MotorControlTask_event &= ~(EVENT_OVER_TEMP1 | EVENT_OVER_TEMP2);
}
TerminateTask();
}
main.c
#include "safe_design.h"
int main(void) {
// 危険値設定(猫設計)
TEMP1_SENSOR_REG = 200; // 異常値
TEMP2_SENSOR_REG = 30;
MOTOR1_REG = 120; // 過電力
MOTOR2_REG = 50;
// タスク実行(演習)
TemperatureMonitor1();
TemperatureMonitor2();
MotorControlTask();
motor_task();
// 安全状態確認
// MOTOR1_REG, MOTOR2_REG が0になっていれば成功
return MOTOR1_REG + MOTOR2_REG;
}
README.md(演習手順例)Freestanding/OSEK 演習教材版 v2
コンパイル方法
gcc main.c safe_design.c -o exercise_v2
演習手順
- main.c の TEMP1_SENSOR_REG, TEMP2_SENSOR_REG, MOTOR1_REG, MOTOR2_REG を変更
- タスクを順に実行(TemperatureMonitor1, TemperatureMonitor2, MotorControlTask, motor_task)
- MOTOR1_REG, MOTOR2_REG が危険値から0にリセットされることを確認
- ガード節、入力検証、フェイルセーフ、OSEKイベントの流れを理解
- 学生は複数センサ・複数モータ制御の安全設計を体験
💡 演習効果
センサー異常時のイベント伝達とフェイルセーフ停止を体験
複数デバイス制御での安全設計パターンを理解
猫設計 → 鳥設計を明確に比較可能
必要であれば、次のステップとして 「ワークショップ用演習プリント」 として
演習手順
質問例
追加課題(新しいセンサーや割り込み制御追加)
までまとめて教材化することもできます。
作りますか?
はい。お願いします。
ChatGPT:
では、次のステップとして 学生向けワークショップ用演習プリント を作成します。
複数センサ・複数モータ制御の安全設計パターンを理解できる内容にまとめます。
📝 ワークショップ用演習プリント(サンプル)
タイトル
「猫設計から鳥設計へ:Freestanding/OSEK制御ソフト安全演習」
目的
制御ソフトウェアにおける安全設計パターン(ガード節、入力検証、フェイルセーフ、イベント駆動)の理解
複数センサ・複数モータ制御の安全設計を体験
猫設計(安全軽視)から鳥設計(安全重視)への改善の効果を体感
演習環境
言語:C言語(Freestanding/OSEK想定)
コンパイル:gcc main.c safe_design.c -o exercise_v2
実行環境:OS依存なし(スタブでOSEKタスクを模擬)
演習ステップ
- 初期状態確認
main.c 内のセンサ値、モータ値を確認
TEMP1_SENSOR_REG = 25;
TEMP2_SENSOR_REG = 30;
MOTOR1_REG = 50;
MOTOR2_REG = 50;
実行結果を確認(MOTOR_REGが変更されないこと)- 猫設計状態を体験
TEMP1_SENSOR_REG を異常値に設定
MOTOR1_REG を過電力に設定
プログラムを実行
結果:MOTOR1_REG, MOTOR2_REG が 0 になり安全停止していることを確認- 安全設計パターンを理解
ガード節:set_motor_power の範囲チェック
入力検証:read_temperature_sensor のセンサ値チェック
フェイルセーフ:motor_task によるモータ停止
イベント駆動:MotorControlTask_event と OSEK タスクによる安全伝達- 課題演習
TEMP2_SENSOR_REG を異常値に設定し、再度安全停止を確認
MOTOR2_REG を過電力に設定し、安全停止を確認
新しいセンサ TEMP3 を追加し、同様に安全設計を組み込む
複数モータ制御のガード節、フェイルセーフ、イベント伝達を設計- 発展課題
割り込み駆動センサを追加して安全停止タイミングを改善
複数センサの平均値を使った過熱検知アルゴリズムを設計
モータ過電流保護のため、PWM制御値を安全上限で制限
学習ポイント
Freestanding環境での安全設計は POSIX 系とは異なる考慮が必要
猫設計と鳥設計の違いを実際のコードで体験することが理解の近道
複数デバイス制御では、イベント伝達とフェイルセーフ設計が重要
💡 講師用ヒント
学生には初めに「MOTOR_REGが危険値」の状態を見せ、フェイルセーフで安全停止することを体験させる
実務哲学「安全・安心・妥当性・公序良俗・省資源」をコード体験に結び付ける
演習後に「猫設計→鳥設計」の改善点をグループで議論させる
希望であれば、このプリントに 図解(タスク・イベント・フェイルセーフの流れ) を追加して、より直感的に理解できる形にして提供できます。
追加で作りますか?
はい。お願いします。