2023年7月19日 星期三

docker-mac Desktop清除容器log

 最近Docker遇到這個問題
Error grabbing logs: invalid character '{' looking for beginning of object key string.

建議的解法是刪除整個log檔,大家可以參考以下教學
https://blog.tkuchiki.net/en/clear-docker-for-mac-logs/

1.請務必開著容器程式

2.找到路徑  docker inspect CONTAINER_ID --format='{{.LogPath}}' 容器代號

3. nc -U ~/Library/Containers/com.docker.docker/Data/debug-shell.sock 

4.rm  對應的log檔

5.重啟應用程式

IoT安全

         IoT(物聯網)的發展,是現今知識時代不可或缺的條件。IoT載物及通訊的特性,使它更加貼近我們生活,智慧家電、無人車、智慧鎖、智慧鞋墊成為近期熱門的發展技術,它們急速改變我們對智慧的認知。廣泛IoT的背後我們也許不曾想過,這正是駭客攻擊發展的溫床,因為它們往往最有價值,相互連結的關係,也許得一就能得十,這些危機更不在侷限於機密資料這麼單純。 IoT安全的議題讓我感到害怕,生活中的我感覺很依賴科技,這會不會導致我被科技利用呢另外,我的專題屬於IoT技術方面,設計系統要往多方面思考,預防問題很重要,不是做出來就好,也許,有天被駭客攻擊導致系統異常,甚至間接影響至其他系統,這就是非常大的危機。

 

經過我進行網路上的議題探討, IoT裝置的漏洞從分析裝置起,攻擊者會先了解IoT裝置類型及相關情報,通常透過特殊的搜尋引擎(ShodanCensys…)達成。第一次聽到的我非常驚訝,這些搜尋引擎竟然專門尋找IoT裝置,接上網的攝影機、影印器還是紅綠燈都能一覽無遺,光透過搜索的結果就可尋找對應的裝置來韌體分析、弱密碼猜測等手法進行攻擊。由於硬體裝置常透過公開的韌體來進行程式的燒錄,又因韌體規格大同小異的關係,攻擊者可針對韌體去找到漏洞或是未移除的後門程式來取得登入權限。

 

IoT造成的危害非常多樣,例如常見的智慧門鎖,它們通常靠無訊訊號來識別PIN碼進行解鎖,我們可能想像這樣比較安全,但完全不是,無線訊號只要對到正確的波段頻率,攻擊者即可透過HackRF等工具進行訊號攔截與重送攻擊,重送攻擊就是最常見的手法,複製按下開鎖的門鎖的訊號再重複發送,即可打開鎖頭,讓陌生人進入家中,可能間接造成社會安全。另外,近期無人車的車聯網發展推陳出新,全自動的駕駛是否可能遭到遠端控制;醫療裝置的使用,紀錄不只包含患者的個資資訊外,甚至部分裝置能危及生命安全,這些案例再次提醒我們IoT安全的重要,不能過度依賴、信任這些技術。

 

我們正面臨IoT大量應用的時代,更應懂得用IoT、懂得安全的保護設定。我們能做的就是將常用的IoT設備定期更換密碼,更新韌體到最新狀態。硬體廠商也應定期進行IoT資安檢測,確認達到標準門檻才能推出產品,並持續加以追蹤,而程式碼也應更詳細的檢查,避免出現不必要的後門程式片段。

 

IoT讓我們的網路更廣更大更深,我想資安的問題只會陸續加重,網路基本的安全將是每個人都必需要知道的常識。我告訴自己,不需再擔心IoT安全,就算我們無法拒絕使用科技,我們仍能積極加強對資安的常識,只要不過度依賴技術,發揮科技的好處與便利性,保護自己再去保護他人,這才是真正的善用科技吧。

2022年2月15日 星期二

體感科技產業介紹-智崴科技副總_賴登鴻演講心得

        演講日期:110年11月17日

        這次的講師是智崴科技的副總賴登鴻先生,聽課前,我認為的體感產業僅單純指VR(虛擬實境)、AR(擴充實境)技術,但這次講座使我大大改觀,了解體感技術的歷史外,更結合我過去的經驗產生不少共鳴,所以挑選這一次講座作為報告分享。


講座開始並沒有開門見山,賴講師與我們分享人生職涯要累積一本存摺,這本存摺要包含態度、人脈與技能,顧好它可以使就業無往不利,更進一步比喻自己從鳥(新手)到駱駝(小公司老闆),最後是鯨魚(大公司副總)的歷程之旅,鼓勵我們投資存摺並朝夢想邁進。


體感產業涉及造景、設計與流程,是一種新媒體遊樂產業,在現有的遊樂設施中,機械的運作有所限制,能帶給顧客的體驗刺激有限,直到近幾年數位影音潮流與機械模擬技術的成長,MBA(Media-Based Attraction)就被發展出來,它是以多媒體形式來建置劇場或遊樂園空間,提供顧客共同乘坐觀賞的新型遊樂設施。


體感裝置從1900s萊特兄弟發明出來,而後1980s採用油壓設計,2000s就開始使用伺服馬達,現有智崴科技的iRide裝置皆使用伺服馬達來運作,我在大學時期有參與過iRide飛越高雄的體驗活動,也體驗過菲律賓遊樂園的iRide,其中的影音內容收錄當地具特色的人文景色,加入體感裝置結合氣味、音效、風、閃電等效果,自己此時就像一隻老鷹飛越上空,整片景觀一覽無遺。我在菲律賓體驗的那場發現別具用心,設備獨立設在一棟具藝術感的建築物內,進場後根據進場順序排隊,內部更結合遊樂園吉祥物,採逐步解說的方式進行動線引導,這樣大大增加顧客的印象與體驗層次。


這樣的裝置其實很不簡單,它必須結合六維、動力以及學理向量xyz軸來做設計,其中技術非常多,光是軟體的參數輸入、產生模型、模擬輸出就非常複雜,舉例來說,在有限的空間模擬無限空間需要Washout filter技術、用學理計算奇異點避免設備損壞、使用PID控制器操作與人因工程設計等等。說到這邊我非常佩服,因為第一台的懸空式體感模擬遊樂設備出自於迪士尼,第二台就是來自台灣的智崴科技公司(與科工館獨家打造)進駐在高雄的義大遊樂世界中,現在去到全球的遊樂園,就可能看到Made in Taiwan的iRide影子。


原本我對iRide的認知就是一二代的型號,講師更分享第三第四代型號,這些新裝置針對中低階市場開發,主要目標就是迴避迪士尼專利、動感裝置體驗(真實度)以及合國際規範的設計,也因為要用在不同的國家進行後續保養維護與管理,他們會撰寫維護手冊並著重於國家安全驗證規範,在商品交貨的七天內會說明,提供技術協助,那未來更會導入雲端與AI技術來追蹤全球各地的裝置,提供即時狀況蒐集與支援。


這次的講座非常充實,很榮幸邀到鼎鼎有名的副總來演講,感到不可思議的是我自己有參訪過iRide,卻沒有印象智崴這個名字,其實他們是待遇水準高的現代公司,若有機會,我也會將這間公司納入未來工作口袋名單。


2021年9月27日 星期一

Flutter wifi_iot範例加簡單註解

Flutter 好用套件wifi_iot
wifi_iot | Flutter Package (pub.dev)
目前版本^0.3.8 Published Sep 22, 2021

Plugin Flutter which can handle WiFi connections (AP, STA)

Becareful, some commands as no effect on iOS because Apple don't let us to do whatever we want...

雖然功能有限,但是做基本的物聯網APP開發已足夠,以下範例取自https://pub.dev/packages/wifi_iot/example 並且我已刪除WIFI STA內容

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:wifi_iot/wifi_iot.dart';
import 'dart:io' show Platform;


const NetworkSecurity AP_DEFAULT_SECURITY = NetworkSecurity.WPA;
const String AP_DEFAULT_SSID = "AP_SSID";
const String AP_DEFAULT_PASSWORD = "AP_PASSWORD";

//父狀態主頁(樣式+Wifi ststus)
class FlutterWifiIoT extends StatefulWidget {
FlutterWifiIoT({
Key? key
,
}) : super(key: key);

@override
_FlutterWifiIoTState createState() => _FlutterWifiIoTState();
//建立status
}

class _FlutterWifiIoTState extends State<FlutterWifiIoT> {


List<WifiNetwork?>?
_htResultNetwork; //WIFI可連清單
Map<String, bool>? _htIsNetworkRegistered = Map(); //WIFI SSID儲存清單與狀態

bool _isEnabled = false; //WIFI是否開啟
bool _isConnected = false; //WIFI是否已連接
bool _isWifiEnableOpenSettings = false; //是否被動開啟(Android 10)
bool _isWifiDisableOpenSettings = false; //是否被動關閉(Android 10)

final TextStyle textStyle = TextStyle(color: Colors.white);

@override
initState() {
WiFiForIoTPlug
in.isEnabled().then((val) {
_isEnabled = val;
});

WiFiForIoTPlugin.isConnected().then((val) {
_isConnected = val;
});

super.initState();
}


//使用函式庫載入WIFI列表
Future<List<WifiNetwork>> loadWifiList() async {
List<WifiNetwork> htResultNetwork
;
try {
htResultNetwork =
await WiFiForIoTPlugin.loadWifiList();
} on PlatformException {
htResultNetwork = <WifiNetwork>[]
;
}

return htResultNetwork;
}

//檢查是否已註冊SSID方法(存到Map ket,value)
isRegisteredWifiNetwork(String ssid) async {
bool bIsRegistered
;

try {
bIsRegistered =
await WiFiForIoTPlugin.isRegisteredWifiNetwork(ssid);
} on PlatformException {
bIsRegistered =
false;
}

setState(() {
_htIsNetworkRegistered![ssid] = bIsRegistered;
});
}

///Main body
Widget getWidgets() {
WiFiForIoTPlug
in.isConnected().then((val) {
setState(() {
_isConnected = val; //WIFI 為已連線狀態
});
});

// disable scanning for ios as not supported
//IOS手機無法支援WIFI清單功能
if (_isConnected || Platform.isIOS) {
_htResultNetwork = null;
}

//如果是Android, 且存在可連線網路, 否則不會載入WIFI清單
if (_htResultNetwork != null && _htResultNetwork!.length > 0) {
List<ListTile> htNetworks = <ListTile>[]
;

_htResultNetwork!.forEach((oNetwork) {
//將可連線網路跑一遍
PopupCommand oCmdConnect = PopupCommand("Connect", oNetwork!.ssid!);
PopupCommand oCmdRemove = PopupCommand("Remove", oNetwork.ssid!);

List<PopupMenuItem<PopupCommand>> htPopupMenuItems = [];

//加到popupMenu(選項為可連線)
htPopupMenuItems.add(
PopupMenuItem<PopupCommand>(
value: oCmdConnect
,
child: const Text('Connect'),
),
);

setState(() {
isRegisteredWifiNetwork(oNetwo
rk.ssid!);
//辨識這個SSID是否已儲存至系統,並在value中用布林表示
if (_htIsNetworkRegistered!.containsKey(oNetwork.ssid) &&
_htIsNetworkRegistered![oNetwork.ssid]!) {
htPopupMenuItems.add(
PopupMenuItem<PopupCommand>(
value: oCmdRemove
,
child: const Text('Remove'),
),//已儲存就可以提供刪除選項
);
}

htNetworks.add(
ListTile(
titl
e: Text("" +
oNetwo
rk.ssid! +
((_htIsNetworkRegistered!.containsKey(oNetwork.ssid) &&
_htIsNetworkRegistered![oNetwork.ssid]!)
?
" *"
: "")),//加入網路名稱,如果已註冊就標示*
trailing: PopupMenuButton<PopupCommand>(
padding: EdgeInse
ts.zero,
onSelected: (PopupCommand poCommand) {
//加入子元素(連線/刪除選項), 會判斷htPopupMenuItems傳進來的value
switch (poCommand.command) {
case "Connect":
WiFiForIoTPlug
in.connect(AP_DEFAULT_SSID,
password: AP_DEFAULT_PASSWORD,
joinOnce: true,
security: AP_DEFAULT_SECURITY);
break;
case "Remove":
WiFiForIoTPlug
in.removeWifiNetwork(poCommand.argument);
break;
default:
break;
}
}
,
itemBuilder: (BuildContext context) => htPopupMenuItems,
),
),
);
});
});

return ListView(
padding: kMaterialListPadding
,
children: htNetworks,
);
} else {
//如果是IOS, Android搜尋結果為0時進入這邊
return SingleChildScrollView(
chil
d: SafeArea(
chil
d: Column(
children: Platfo
rm.isIOS
? getButtonWidgetsForiOS()
: getButtonWidgetsForAndroid()
,
),
),
);
}
}

List<Widge
t> getButtonWidgetsForAndroid() {
List<Widget> htPrimaryWidgets = <Widget>[]
;

WiFiForIoTPlugin.isEnabled().then((val) {
setState(() {
_isEnabled = val;
});
});

if (_isEnabled) {
htPrimaryWidgets.addAll([
SizedBox(height: 10),
Text("Wifi Enabled"),
MaterialButton(
color: Colo
rs.blue,
child: Text("Disable", style: textStyle),
onPressed: () {
WiFiForIoTPlug
in.setEnabled(false,
shouldOpenSettings: _isWifiDisableOpenSettings
)
;
},
),
]);

WiFiForIoTPlugin.isConnected().then((val) {
setState(() {
_isConnected = val;
});
});

if (_isConnected) {
//已經連上Wifi, 回傳連線資訊
htPrimaryWidgets.addAll(<Widget>[
Text("Connected"),
FutureBuilder(
future: WiFiForIoTPlug
in.getSSID(),
initialData: "Loading..",
builder: (BuildContext context, AsyncSnapshot<String?> ssid) {
return Text("SSID: ${ssid.data}");
}),
FutureBuilder(
future: WiFiForIoTPlug
in.getBSSID(),
initialData: "Loading..",
builder: (BuildContext context, AsyncSnapshot<String?> bssid) {
return Text("BSSID: ${bssid.data}");
}),
FutureBuilder(
future: WiFiForIoTPlug
in.getCurrentSignalStrength(),
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int?> signal) {
return Text("Signal: ${signal.data}");
}),
FutureBuilder(
future: WiFiForIoTPlug
in.getFrequency(),
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int?> freq) {
return Text("Frequency : ${freq.data}");
}),
FutureBuilder(
future: WiFiForIoTPlug
in.getIP(),
initialData: "Loading..",
builder: (BuildContext context, AsyncSnapshot<String?> ip) {
return Text("IP : ${ip.data}");
}),
MaterialButton(
color: Colo
rs.blue,
child: Text("Disconnect", style: textStyle),
onPressed: () {
WiFiForIoTPlug
in.disconnect();
},
),
CheckboxListTile(
title:
const Text("Disable WiFi on settings"),
subtitle: const Text("Available only on android API level >= 29"),
value: _isWifiDisableOpenSettings,
onChanged: (bool? setting) {
if (setting != null) {
setState(() {
_isWifiDisableOpenSettings = setting;
});
}
}
)
//Android API29以上可以被動接收WIFI info
]);
} else {
//未連上WIFI, 顯示WIFI搜尋
htPrimaryWidgets.addAll(<Widget>[
Text("Disconnected"),
MaterialButton(
color: Colo
rs.blue,
child: Text("Scan", style: textStyle),
onPressed: () async {
_htResultNetwork = await loadWifiList();
setState(() {});
},
),
CheckboxListTile(
title:
const Text("Disable WiFi on settings"),
subtitle: const Text("Available only on android API level >= 29"),
value: _isWifiDisableOpenSettings,
onChanged: (bool? setting) {
if (setting != null) {
setState(() {
_isWifiDisableOpenSettings = setting;
});
}
}
)
])
;
}
}
else {
//WIFI設定未開啟
htPrimaryWidgets.addAll(<Widget>[
SizedBox(height: 10),
Text("Wifi Disabled"),
MaterialButton(
color: Colo
rs.blue,
child: Text("Enable", style: textStyle),
onPressed: () {
setState(() {
WiFiForIoTPlug
in.setEnabled(true,
shouldOpenSettings: _isWifiEnableOpenSettings
)
;
});
},
),
CheckboxListTile(
title:
const Text("Enable WiFi on settings"),
subtitle: const Text("Available only on android API level >= 29"),
value: _isWifiEnableOpenSettings,
onChanged: (bool? setting) {
if (setting != null) {
setState(() {
_isWifiEnableOpenSettings = setting;
});
}
}
)
])
;
}

return htPrimaryWidgets;
}

List<Widge
t> getButtonWidgetsForiOS() {
List<Widget> htPrimaryWidgets = <Widget>[]
;

WiFiForIoTPlugin.isEnabled().then((val) => setState(() {
_isEnabled = val;
}));

if (_isEnabled) {
htPrimaryWidgets.a
dd(Text("Wifi Enabled"));
WiFiForIoTPlugin.isConnected().then((val) => setState(() {
_isConnected = val;
}));

String? _sSSID;

if (_isConnected) {
//IOS 顯示連線(SSID)
htPrimaryWidgets.addAll(<Widget>[
Text("Connected"),
FutureBuilder(
future: WiFiForIoTPlug
in.getSSID(),
initialData: "Loading..",
builder: (BuildContext context, AsyncSnapshot<String?> ssid) {
_sSSID = ss
id.data;

return Text("SSID: ${ssid.data}");
}),
]);

if (_sSSID == AP_DEFAULT_SSID) {
//IOS連上指定SSID
htPrimaryWidgets.addAll(<Widget>[
MaterialButton(
color: Colo
rs.blue,
child: Text("Disconnect", style: textStyle),
onPressed: () {
WiFiForIoTPlug
in.disconnect();
},
),
]);
} else {
//IOS未上指定SSID, 提供按鈕直接連
htPrimaryWidgets.addAll(<Widget>[
MaterialButton(
color: Colo
rs.blue,
child: Text("Connect to '$AP_DEFAULT_SSID'", style: textStyle),
onPressed: () {
WiFiForIoTPlug
in.connect(AP_DEFAULT_SSID,
password: AP_DEFAULT_PASSWORD,
joinOnce: true,
security: NetworkSecurity.WPA);
},
),
]);
}
}
else {
//IOS未上網路, 一樣提供按鈕直接連
htPrimaryWidgets.addAll(<Widget>[
Text("Disconnected"),
MaterialButton(
color: Colo
rs.blue,
child: Text("Connect to '$AP_DEFAULT_SSID'", style: textStyle),
onPressed: () {
WiFiForIoTPlug
in.connect(AP_DEFAULT_SSID,
password: AP_DEFAULT_PASSWORD,
joinOnce: true,
security: NetworkSecurity.WPA);
},
),
]);
}
}
else {
//IOS WIFI是關閉的, 給設定可以硬連
htPrimaryWidgets.addAll(<Widget>[
Text("Wifi Disabled?"),
MaterialButton(
color: Colo
rs.blue,
child: Text("Connect to '$AP_DEFAULT_SSID'", style: textStyle),
onPressed: () {
WiFiForIoTPlug
in.connect(AP_DEFAULT_SSID,
password: AP_DEFAULT_PASSWORD,
joinOnce: true,
security: NetworkSecurity.WPA);
},
),
]);
}

return htPrimaryWidgets;
}

@override
Widg
et build(BuildContext poContext) {
return MaterialApp(
title: Platfo
rm.isIOS ?
"WifiFlutter Example iOS" : "WifiFlutter Example Android",
home: Scaffold(
appBa
r: AppBar(
title: Platfo
rm.isIOS
? Text('WifiFlutter Example iOS')
: Text('WifiFlutter Example Android'),
actions: _isConnected
? <Widget>[
PopupMenuButton<String>(
onSelected: (value) {
switch (value) {
case "disconnect":
WiFiForIoTPlug
in.disconnect();
break;
case "remove":
WiFiForIoTPlug
in.getSSID().then(
(val) => WiFiForIoTPlug
in.removeWifiNetwork(val!));
break;
default:
break;
}
}
,
itemBuilder: (BuildContext context) =>
<PopupMenuItem<String>>[
PopupMenuItem<String>(
value:
"disconnect",
child: const Text('Disconnect'),
),
PopupMenuItem<String>(
value:
"remove",
child: const Text('Remove'),
),
],
),
]
:
null,
),
body: getWidgets(),
),
);
}
}

class PopupCommand {
Stri
ng command;
String argument;

PopupCommand(this.command, this.argument);
}

//頁面樣式(無狀態)
class CardDemo extends StatelessWidget {
final TextEditingController _textFieldController = TextEditingController();
final List listData = [
{
"title": "物料站 1 ",
"context": "相關編號....",
},
{
"title": "物料站 2 ",
"context": "相關編號....",
}
]
;

@override
Widg
et build(BuildContext context) {
return ListView(
childre
n: listData.map((value) {
return Card(
margi
n: EdgeInsets.all(10), //外邊距
child: Column(
//縱向排列元件
children: <Widget>[
ListTile(
titl
e: Text(value["title"], style: TextStyle(fontSize: 28)),
subtitle: Text(value["context"],
overflow: TextOverflow.ellipsis,
maxLines: 2, //文字溢位
style: TextStyle(fontSize: 18, color: Colors.black)),
onTap: () => _displayDialog(context),
),
],
),
);
}).toList(),
);
}

_displayDialog(BuildContext context) async {
return showDialog(
context: context
,
builder: (context) {
return AlertDialog(
titl
e: Text('Material Password'),
content: TextField(
controlle
r: _textFieldController,
textInputAction: TextInputAction.go,
keyboardType: TextInputType.numberWithOptions(),
decoration: InputDecoration(hintText: "Password"),
),
actions: <Widget>[
new FlatButton(
child:
new Text('Cancel'),
onPressed: () {
Navigat
or.of(context).pop();
},
),
new FlatButton(
child:
new Text('Comfirm'),
onPressed: () {
_displayDialog2(context)
;
},
)
]
,
);
});
}

_displayDialog2(BuildContext context) async {
return showDialog(
context: context
,
builder: (context) {
return AlertDialog(
titl
e: Text('Material Name'),
content: TextField(
controlle
r: _textFieldController,
textInputAction: TextInputAction.go,
keyboardType: TextInputType.numberWithOptions(),
decoration: InputDecoration(hintText: "Password"),
),
actions: <Widget>[
new FlatButton(
child:
new Text('Cancel'),
onPressed: () {
Navigat
or.of(context).pop();
},
),
new FlatButton(
child:
new Text('Comfirm'),
onPressed: () {
Navigat
or.push(context,
MaterialPageRoute(builder: (context) => XDChooseWifi()));
},
)
]
,
);
});
}

} 

docker-mac Desktop清除容器log

 最近Docker遇到這個問題 Error grabbing logs: invalid character '{' looking for beginning of object key string. 建議的解法是刪除整個log檔,大家可以參考以下教學 htt...