vault backup: 2025-08-20 14:35:48

Affected files:
Too many files to list
This commit is contained in:
2025-08-20 14:35:48 +08:00
parent 1d09289423
commit afeb207be2
148 changed files with 13360 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
{
"theme": "moonstone",
"cssTheme": "",
"baseFontSize": 13,
"translucency": false,
"enabledCssSnippets": [
"obsidian"
],
"baseFontSizeAction": true,
"interfaceFontFamily": "",
"textFontFamily": "Verdana,Microsoft YaHei UI",
"monospaceFontFamily": "Cascadia Code",
"accentColor": ""
}

View File

@@ -0,0 +1,32 @@
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": false,
"outgoing-link": true,
"tag-pane": true,
"page-preview": true,
"daily-notes": true,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": true,
"editor-status": true,
"starred": false,
"markdown-importer": true,
"zk-prefixer": true,
"random-note": true,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": true,
"file-recovery": true,
"publish": false,
"sync": false,
"canvas": true,
"bookmarks": true,
"properties": true,
"webviewer": false
}

View File

@@ -0,0 +1,66 @@
{
"editor:fold-all": [
{
"modifiers": [
"Mod",
"Shift"
],
"key": "ArrowLeft"
}
],
"editor:unfold-all": [
{
"modifiers": [
"Mod",
"Shift"
],
"key": "ArrowRight"
}
],
"editor:toggle-fold": [
{
"modifiers": [
"Mod",
"Shift"
],
"key": "PageDown"
}
],
"obsidian-tasks-plugin:edit-task": [
{
"modifiers": [
"Alt",
"Mod"
],
"key": "S"
}
],
"obsidian-tasks-plugin:toggle-done": [
{
"modifiers": [
"Alt",
"Mod"
],
"key": "D"
}
],
"editor:open-link-in-new-leaf": [
{
"modifiers": [
"Alt",
"Mod"
],
"key": "Enter"
}
],
"daily-notes": [],
"insert-template": [
{
"modifiers": [
"Mod",
"Shift"
],
"key": "-"
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
{
"author": "Vinzent",
"authorUrl": "https://github.com/Vinzent03",
"id": "obsidian-git",
"name": "Git",
"description": "Integrate Git version control with automatic backup and other advanced features.",
"isDesktopOnly": false,
"fundingUrl": "https://ko-fi.com/vinzent",
"version": "2.32.1"
}

View File

@@ -0,0 +1,576 @@
@keyframes loading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.workspace-leaf-content[data-type="git-view"] .button-border {
border: 2px solid var(--interactive-accent);
border-radius: var(--radius-s);
}
.workspace-leaf-content[data-type="git-view"] .view-content {
padding: 0;
}
.workspace-leaf-content[data-type="git-history-view"] .view-content {
padding: 0;
}
.loading > svg {
animation: 2s linear infinite loading;
transform-origin: 50% 50%;
display: inline-block;
}
.obsidian-git-center {
margin: auto;
text-align: center;
width: 50%;
}
.obsidian-git-textarea {
display: block;
margin-left: auto;
margin-right: auto;
}
.obsidian-git-disabled {
opacity: 0.5;
}
.obsidian-git-center-button {
display: block;
margin: 20px auto;
}
.tooltip.mod-left {
overflow-wrap: break-word;
}
.tooltip.mod-right {
overflow-wrap: break-word;
}
.git-tools {
display: flex;
margin-left: auto;
}
.git-tools .type {
padding-left: var(--size-2-1);
display: flex;
align-items: center;
justify-content: center;
width: 11px;
}
.git-tools .type[data-type="M"] {
color: orange;
}
.git-tools .type[data-type="D"] {
color: red;
}
.git-tools .buttons {
display: flex;
}
.git-tools .buttons > * {
padding: 0 0;
height: auto;
}
.is-active .git-tools .buttons > * {
color: var(--nav-item-color-active);
}
.git-author {
color: var(--text-accent);
}
.git-date {
color: var(--text-accent);
}
.git-ref {
color: var(--text-accent);
}
.workspace-leaf-content[data-type="diff-view"] .d2h-d-none {
display: none;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-wrapper {
text-align: left;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-header {
background-color: var(--background-primary);
border-bottom: 1px solid var(--interactive-accent);
font-family: var(--font-monospace);
height: 35px;
padding: 5px 10px;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-header,
.workspace-leaf-content[data-type="diff-view"] .d2h-file-stats {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-stats {
font-size: 14px;
margin-left: auto;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-lines-added {
border: 1px solid #b4e2b4;
border-radius: 5px 0 0 5px;
color: #399839;
padding: 2px;
text-align: right;
vertical-align: middle;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-lines-deleted {
border: 1px solid #e9aeae;
border-radius: 0 5px 5px 0;
color: #c33;
margin-left: 1px;
padding: 2px;
text-align: left;
vertical-align: middle;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-name-wrapper {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
font-size: 15px;
width: 100%;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-name {
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-wrapper {
border: 1px solid var(--background-modifier-border);
border-radius: 3px;
margin-bottom: 1em;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse {
-webkit-box-pack: end;
-ms-flex-pack: end;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
border: 1px solid var(--background-modifier-border);
border-radius: 3px;
cursor: pointer;
display: none;
font-size: 12px;
justify-content: flex-end;
padding: 4px 8px;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse.d2h-selected {
background-color: #c8e1ff;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse-input {
margin: 0 4px 0 0;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-diff-table {
border-collapse: collapse;
font-family: Menlo, Consolas, monospace;
font-size: 13px;
width: 100%;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-files-diff {
width: 100%;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-diff {
overflow-y: hidden;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-side-diff {
display: inline-block;
margin-bottom: -8px;
margin-right: -4px;
overflow-x: scroll;
overflow-y: hidden;
width: 50%;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line {
padding: 0 8em;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line,
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line {
display: inline-block;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
white-space: nowrap;
width: 100%;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line {
padding: 0 4.5em;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line-ctn {
word-wrap: normal;
background: none;
display: inline-block;
padding: 0;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
vertical-align: middle;
white-space: pre;
width: 100%;
}
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-line del,
.theme-light
.workspace-leaf-content[data-type="diff-view"]
.d2h-code-side-line
del {
background-color: #ffb6ba;
}
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-line del,
.theme-dark
.workspace-leaf-content[data-type="diff-view"]
.d2h-code-side-line
del {
background-color: #8d232881;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line del,
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins,
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line del,
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line ins {
border-radius: 0.2em;
display: inline-block;
margin-top: -1px;
text-decoration: none;
vertical-align: middle;
}
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins,
.theme-light
.workspace-leaf-content[data-type="diff-view"]
.d2h-code-side-line
ins {
background-color: #97f295;
text-align: left;
}
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins,
.theme-dark
.workspace-leaf-content[data-type="diff-view"]
.d2h-code-side-line
ins {
background-color: #1d921996;
text-align: left;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line-prefix {
word-wrap: normal;
background: none;
display: inline;
padding: 0;
white-space: pre;
}
.workspace-leaf-content[data-type="diff-view"] .line-num1 {
float: left;
}
.workspace-leaf-content[data-type="diff-view"] .line-num1,
.workspace-leaf-content[data-type="diff-view"] .line-num2 {
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
padding: 0 0.5em;
text-overflow: ellipsis;
width: 3.5em;
}
.workspace-leaf-content[data-type="diff-view"] .line-num2 {
float: right;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber {
background-color: var(--background-primary);
border: solid var(--background-modifier-border);
border-width: 0 1px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: var(--text-muted);
cursor: pointer;
display: inline-block;
position: absolute;
text-align: right;
width: 7.5em;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber:after {
content: "\200b";
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber {
background-color: var(--background-primary);
border: solid var(--background-modifier-border);
border-width: 0 1px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: var(--text-muted);
cursor: pointer;
display: inline-block;
overflow: hidden;
padding: 0 0.5em;
position: absolute;
text-align: right;
text-overflow: ellipsis;
width: 4em;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-diff-tbody tr {
position: relative;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber:after {
content: "\200b";
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-emptyplaceholder,
.workspace-leaf-content[data-type="diff-view"] .d2h-emptyplaceholder {
background-color: var(--background-primary);
border-color: var(--background-modifier-border);
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line-prefix,
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber,
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber,
.workspace-leaf-content[data-type="diff-view"] .d2h-emptyplaceholder {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber,
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber {
direction: rtl;
}
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-del {
background-color: #fee8e9;
border-color: #e9aeae;
}
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-ins {
background-color: #dfd;
border-color: #b4e2b4;
}
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-del {
background-color: #521b1d83;
border-color: #691d1d73;
}
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-ins {
background-color: rgba(30, 71, 30, 0.5);
border-color: #13501381;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-info {
background-color: var(--background-primary);
border-color: var(--background-modifier-border);
color: var(--text-normal);
}
.theme-light
.workspace-leaf-content[data-type="diff-view"]
.d2h-file-diff
.d2h-del.d2h-change {
background-color: #fdf2d0;
}
.theme-dark
.workspace-leaf-content[data-type="diff-view"]
.d2h-file-diff
.d2h-del.d2h-change {
background-color: #55492480;
}
.theme-light
.workspace-leaf-content[data-type="diff-view"]
.d2h-file-diff
.d2h-ins.d2h-change {
background-color: #ded;
}
.theme-dark
.workspace-leaf-content[data-type="diff-view"]
.d2h-file-diff
.d2h-ins.d2h-change {
background-color: rgba(37, 78, 37, 0.418);
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-wrapper {
margin-bottom: 10px;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-wrapper a {
color: #3572b0;
text-decoration: none;
}
.workspace-leaf-content[data-type="diff-view"]
.d2h-file-list-wrapper
a:visited {
color: #3572b0;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-header {
text-align: left;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-title {
font-weight: 700;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-line {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
text-align: left;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list {
display: block;
list-style: none;
margin: 0;
padding: 0;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list > li {
border-bottom: 1px solid var(--background-modifier-border);
margin: 0;
padding: 5px 10px;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list > li:last-child {
border-bottom: none;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-file-switch {
cursor: pointer;
display: none;
font-size: 10px;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-icon {
fill: currentColor;
margin-right: 10px;
vertical-align: middle;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-deleted {
color: #c33;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-added {
color: #399839;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-changed {
color: #d0b44c;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-moved {
color: #3572b0;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-tag {
background-color: var(--background-primary);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
font-size: 10px;
margin-left: 5px;
padding: 0 2px;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-deleted-tag {
border: 2px solid #c33;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-added-tag {
border: 1px solid #399839;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-changed-tag {
border: 1px solid #d0b44c;
}
.workspace-leaf-content[data-type="diff-view"] .d2h-moved-tag {
border: 1px solid #3572b0;
}
/* ====================== Line Authoring Information ====================== */
.cm-gutterElement.obs-git-blame-gutter {
/* Add background color to spacing inbetween and around the gutter for better aesthetics */
border-width: 0px 2px 0.2px 2px;
border-style: solid;
border-color: var(--background-secondary);
background-color: var(--background-secondary);
}
.cm-gutterElement.obs-git-blame-gutter > div,
.line-author-settings-preview {
/* delegate text color to settings */
color: var(--obs-git-gutter-text);
font-family: monospace;
height: 100%; /* ensure, that age-based background color occupies entire parent */
text-align: right;
padding: 0px 6px 0px 6px;
white-space: pre; /* Keep spaces and do not collapse them. */
}
@media (max-width: 800px) {
/* hide git blame gutter not to superpose text */
.cm-gutterElement.obs-git-blame-gutter {
display: none;
}
}
.git-unified-diff-view,
.git-split-diff-view .cm-deletedLine .cm-changedText {
background-color: #ee443330;
}
.git-unified-diff-view,
.git-split-diff-view .cm-insertedLine .cm-changedText {
background-color: #22bb2230;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
{
"id": "obsidian-quiet-outline",
"name": "Quiet Outline",
"version": "0.3.43",
"minAppVersion": "0.15.6",
"description": "Make outline quiet and more powerful, including no-auto-expand, rendering heading as markdown, and search support.",
"author": "the_tree",
"authorUrl": "",
"fundingUrl": "https://www.buymeacoffee.com/thtree",
"isDesktopOnly": false
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
{
"id": "obsidian-rollover-daily-todos",
"name": "Rollover Daily Todos",
"version": "1.1.8",
"minAppVersion": "0.12.12",
"description": "This Obsidian.md plugin rolls over incomplete TODOs from the previous daily note to today's daily note. (https://obsidian.md). (Originally created by Matthew Sessions)",
"author": "Lukas Mölschl",
"authorUrl": "https://moelschl.com",
"isDesktopOnly": false
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
{
"id": "obsidian-tasks-plugin",
"name": "Tasks",
"version": "7.19.0",
"minAppVersion": "1.4.0",
"description": "Track tasks across your vault. Supports due dates, recurring tasks, done dates, sub-set of checklist items, and filtering.",
"helpUrl": "https://publish.obsidian.md/tasks/",
"author": "Clare Macrae and Ilyas Landikov (created by Martin Schenck)",
"authorUrl": "https://github.com/obsidian-tasks-group",
"fundingUrl": "https://github.com/sponsors/claremacrae",
"isDesktopOnly": false
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
{
"id": "obsidian-tracker",
"name": "Tracker",
"version": "1.15.1",
"minAppVersion": "0.9.12",
"description": "A plugin tracks occurrences and numbers in your notes",
"author": "pyrochlore",
"authorUrl": "",
"isDesktopOnly": false
}

View File

@@ -0,0 +1,283 @@
{
"main": {
"id": "fd76501ab5551c59",
"type": "split",
"children": [
{
"id": "be9108bba4e1bc4f",
"type": "tabs",
"children": [
{
"id": "fe0f597a8a71c75a",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "10. 日記/2025-05-05(週一).md",
"mode": "source",
"source": true,
"backlinks": false
},
"icon": "lucide-file",
"title": "2025-05-05(週一)"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "c03b931709aac93a",
"type": "split",
"children": [
{
"id": "262a6c92848bc73a",
"type": "tabs",
"children": [
{
"id": "f69acad103e7f817",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "檔案瀏覽器"
}
},
{
"id": "bad194a4534ef74b",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "closur",
"matchingCase": false,
"explainSearch": true,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "搜尋"
}
},
{
"id": "e41d3ba9ac328959",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "書籤"
}
}
]
}
],
"direction": "horizontal",
"width": 249.5
},
"right": {
"id": "bb4f1f6a5dddbb12",
"type": "split",
"children": [
{
"id": "e04f7f5c2b67b828",
"type": "tabs",
"children": [
{
"id": "bac9d59fdcd09bd8",
"type": "leaf",
"state": {
"type": "advanced-tables-toolbar",
"state": {},
"icon": "spreadsheet",
"title": "Advanced Tables"
}
},
{
"id": "61119a6658ef61a6",
"type": "leaf",
"state": {
"type": "all-properties",
"state": {
"sortOrder": "frequency",
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-archive",
"title": "所有屬性"
}
},
{
"id": "d24e22dd36a5a962",
"type": "leaf",
"state": {
"type": "file-properties",
"state": {
"file": "01.00. Me/00. ⚡ TODO.md"
},
"icon": "lucide-info",
"title": "00. ⚡ TODO 的檔案屬性"
}
},
{
"id": "71c00990f5312358",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"file": "00. Inbox/Stock Info.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Stock Info 的對外連結"
}
},
{
"id": "13572b452cf83f13",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "10. 日記/2025-05-05(週一).md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "2025-05-05(週一) 的大綱"
}
},
{
"id": "29e864ba839125e4",
"type": "leaf",
"state": {
"type": "copilot-chat-view",
"state": {},
"icon": "message-square",
"title": "Copilot"
}
}
],
"currentTab": 4
},
{
"id": "ae4bf98badbfc7ee",
"type": "tabs",
"children": [
{
"id": "18b9707a37e1188a",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "標籤"
}
},
{
"id": "6f1990f1d5390883",
"type": "leaf",
"state": {
"type": "calendar",
"state": {},
"icon": "calendar-with-checkmark",
"title": "Calendar"
}
}
],
"currentTab": 1
}
],
"direction": "horizontal",
"width": 297.5
},
"left-ribbon": {
"hiddenItems": {
"switcher:開啟快速切換": false,
"graph:查看關聯圖": false,
"canvas:建立新畫布": false,
"daily-notes:開啟今天的每日筆記": false,
"templates:插入模板": false,
"command-palette:開啟命令面板": false,
"markdown-importer:開啟 Markdown 格式轉換器": false,
"random-note:開始漫遊筆記": false,
"workspaces:管理工作區配置": false,
"zk-prefixer:建立新唯一筆記": false,
"table-editor-obsidian:Advanced Tables Toolbar": false,
"webpage-html-export:Export Vault to HTML": false,
"obsidian-git:Open Git source control": false,
"copilot:Open Copilot Chat": false
}
},
"active": "fe0f597a8a71c75a",
"lastOpenFiles": [
"22.01. 軟體工具、設定/Visual Stuio Code + Msys2.md",
"10. 日記/2025-05-05(週一).md",
"22.01. 軟體工具、設定/Visual Studio Code.md",
"21.02. Windows/Msys2.md",
"attachments/20250505_101809_Obsidian_267x313.png",
"attachments/20250505_101709_chrome_844x478.png",
"attachments/20250505_101427_SystemPropertiesAdvanced_677x633.png",
"attachments/20250505_101259_SystemPropertiesAdvanced_839x208.png",
"21.02. Windows/Windows 11 重灌.md",
"00.01. 雜/名言佳句.md",
"00.01. 雜/知識管理.md",
"00. Inbox/如何打造一個華麗又實用的 PowerShell 命令輸入環境.md",
"00. Inbox/00. 🐧可以試試看.md",
"00. Inbox/筆記:全面掌握 GitHub Copilot 代理人模式:打造專屬 AI 開發助手.md",
"00. Inbox/筆記GitHub Copilot 協作開發實戰.md",
"00. Inbox/筆記RobotRunPackage Refactor.md",
"00. Inbox/奧斯本效應.md",
"00. Inbox/Custom instructions for GitHub Copilot in VS Code.md",
"00. Inbox/Microsoft 365 Pro Plus Retail IMG+KMS 檔.md",
"00. Inbox/Themes Oh My Posh.md",
"22.01. 軟體工具、設定/Windows Terminal.md",
"00.01. 雜/ATP.md",
"00. Inbox/請問 tasks.json 要怎麼用sitecode.visualstudio.com.md",
"20.02. CPP/lambda.md",
"00. Inbox/安裝新版 Oh My Posh 與插件來美化 PowerShell.md",
"21.01. Linux/lvm.md",
"21.01. Linux/Docker.md",
"21.01. Linux/_Map.canvas",
"21.01. Linux/00. 重灌基本步驟.md",
"21.01. Linux/更改時區.md",
"00. Inbox/股票/個股/日月光.md",
"00. Inbox/股票/產業領域",
"00. Inbox/股票/個股",
"00. Inbox/股票",
"23.02. 樹莓派/_Map.canvas",
"attachments/___m365_005.png",
"attachments/___m365_004.png",
"attachments/Pasted image 20250406193841 1.png",
"attachments/Pasted image 20250406193841.png",
"attachments/___m365_003.png",
"attachments/___m365_002.png",
"00. Inbox/Nvidia/新聞",
"00. Inbox/Nvidia",
"00. Inbox/FFMPEG.canvas",
"00. Inbox/MediaFoundation.canvas",
"02. 個人Daily/desktop.ini",
"02. 個人Daily",
"00.01. 雜/flask.canvas",
"00.01. 雜/魚病.canvas",
"00.01. 雜/挖礦.canvas",
"00.01. 雜/自我成長.canvas",
"21.02. Windows/_Map.canvas",
"21.01. Linux/架站/_Server Map.canvas",
"00.02. 給孩子的簡報",
"01.00. Me",
"21.03. MAC"
]
}

13
00.01. 雜/AI Prompt.md Normal file
View File

@@ -0,0 +1,13 @@
---
tags:
aliases:
date: 2024-06-08
time: 12:48:44
description:
---
## Prompt
### 總結論文
請以markdown心智圖筆記的概念來為我整理這份pdf文件從文件目錄結構作為第一層向下深入整理出重要的知識點、研究方法、研究成果、實驗數據洞察、結論輸出包括階層式標題筆記內容以及可以豐富視覺化的表情符號。
# 參考來源

3
00.01. 雜/ATP.md Normal file
View File

@@ -0,0 +1,3 @@
# 三磷酸腺苷
**三磷酸腺苷**英語adenosine triphosphate、[縮寫](https://zh.wikipedia.org/wiki/%E7%B8%AE%E5%AF%AB "縮寫")ATP也稱作**腺苷三磷酸**、**腺嘌呤核苷三磷酸**、**腺嘌呤三磷酸核糖核苷酸**,在[生物化學](https://zh.wikipedia.org/wiki/%E7%94%9F%E7%89%A9%E5%8C%96%E5%AD%B8 "生物化學")中是一種[核苷酸](https://zh.wikipedia.org/wiki/%E6%A0%B8%E8%8B%B7%E9%85%B8 "核苷酸"),作為[細胞](https://zh.wikipedia.org/wiki/%E7%B4%B0%E8%83%9E "細胞")內[能量](https://zh.wikipedia.org/wiki/%E8%83%BD%E9%87%8F "能量")傳遞的「能量貨幣」,儲存和傳遞[化學能](https://zh.wikipedia.org/wiki/%E5%8C%96%E5%AD%A6%E8%83%BD "化學能")。ATP在[核酸](https://zh.wikipedia.org/wiki/%E6%A0%B8%E9%85%B8 "核酸")合成中也具有重要作用。它也是[RNA](https://zh.wikipedia.org/wiki/RNA "RNA")序列中的鳥嘌呤二核苷酸,在[DNA](https://zh.wikipedia.org/wiki/DNA "DNA")進行[轉錄](https://zh.wikipedia.org/wiki/%E8%BD%89%E9%8C%84 "轉錄")時可做為替補。

View File

@@ -0,0 +1,9 @@
- [布朗噪音(「棕色噪音」)和白噪音有何卻別?為什麼可能幫助多動症者? - BBC News 中文](https://www.bbc.com/zhongwen/trad/science-63342282)
布朗噪聲也稱布朗噪音、棕色噪音、紅色噪音或隨機移動噪音brown noise相對於其「表親」白噪音[[White noise]]布朗噪音相對鮮為人知而且對其研究也比較少。據說這種聲音可以幫助集中精力有助睡眠甚至可以幫助那些患有神經多樣性病症例如注意力缺陷多動症俗稱多動症ADHD的人自我調節。
但英文brown noise中的「brown」在這裏並不是指棕色雖然後來也有人把它稱為棕色噪聲而是指布朗運動Brownian motion。幾乎在200年前的1827年植物學家羅伯特·布朗(Robert Brown)首次發現花粉在水中做(不規則)運動。
後來證實其他微細顆粒如灰塵也有同樣的現象,雖然他並沒有能從理論上解釋這種現象,但後來的科學家用他的名字命名為布朗運動,因為該聲音信號的圖形表現跟布朗模式一摸一樣,故此得名。
布朗噪音有時也被稱為紅噪音,其來源於光的類比,紅光比白光有更多低頻波譜,就像布朗噪音比白爆音具有更多低頻波一樣。

View File

@@ -0,0 +1,13 @@
# Set donation to 0
Open `xmrig/src/donate.h`, set `kDefaultDonateLevel` to 0, and set `kMinimumDonateLevel` to 0.
# Build
```bash
git clone https://github.com/xmrig/xmrig.git
mkdir xmrig\build && cd xmrig\build
cmake .. -G "Visual Studio 17 2022" -A x64 -DXMRIG_DEPS=c:\xmrig-deps\msvc2019\x64
cmake --build . --config Release
```
# 參考
- [Windows build - XMRig](https://xmrig.com/docs/miner/build/windows)

15
00.01. 雜/GPU over IP.md Normal file
View File

@@ -0,0 +1,15 @@
---
tags:
aliases:
date: 2025-02-02
time: 21:05:22
description:
---
# SCUDA
[SCUDA](https://github.com/kevmo314/scuda?tab=readme-ov-file#scuda-gpu-over-ip) is a GPU over IP bridge allowing GPUs on remote machines to be attached to CPU-only machines.
# Juice
[Juice](https://github.com/Juice-Labs/Juice-Labs/wiki) is **GPU-over-IP**。可以讓GPU不夠強大的電腦使用另一台電腦的GPU來運算或是玩遊戲。
# 參考來源

View File

@@ -0,0 +1,37 @@
GT1545T
- G: Gitzo
- T: 類型
- T: 三腳架
- M: 單腳架
- H: 雲台
- K: 三腳架套裝
- 1: 第一節腳管的粗度
- 0: 20mm
- 1: 24mm
- 2: 28mm
- 3: 32mm
- 4: 37mm
- 5: 41mm
- 5: 碳纖維
- 3: 鋁合金
- 5: 碳纖維
- 7: 鎂合金
- 8: 玄武岩纖維
- 4: 腳管節數
- 3: 3 節
- 4: 4 節
- 5: 5 節
- 6: 6 節
- 5: 版本
- 0: 第一版
- 1: 第二版
- 2: 第三版
- 3: 第四版
- 後面的英文字: 額外資料
- S: Systematic 系列
- L: Long 延伸後較長
- XL: Extra Long 延伸後更長
- G: Giant 延伸後長如巨人
- V: Video 附有錄影用碗形轉接器
- T: Traveller 收合後較細小

38
00.01. 雜/HEIF.md Normal file
View File

@@ -0,0 +1,38 @@
## 什麼是 HEIF / HEIC
HEIF 是儲存照片的格式全名是「High Efficiency Image File」(高效影像檔),是 HEVC/H.265 (High Efficiency Video Coding高效影片編碼) 的硬照版本;而 HEIC (High Efficiency Image Container高效影像容器) 則是蘋果用來形容 HEIF 格式檔的副檔名。 無可否認,「容器格式」(Container Format) 比起「格式」二字更能表達 HEIF 的能耐。
## HEIF 較小較佳
HEIF 檔案的大小約是 JPEG 檔案的一半,意味大家可以相同儲存空間放置多一倍的照片,也可縮短從網絡上傳或下載照片的時間,這是 HEIF 帶給我們最大的好處之一。 別以為較小的檔案會導致較差的畫質相反HEIF 的影像質素比 JPEG 還要好。單是這兩個好處就值得推翻「統治」硬照超過 25 年的 JPEG。
何解 HEIF 可以較小的儲存空間來記錄更多畫面細節呢?不要忘記 JPEG 是 1992 年的產物。經過二十多年科技的進步,不論是電腦硬件還是影像壓縮演算法,都獲得了空前成功和突破。 當年也有很多出色的影像壓縮演算法,礙於算式過於複雜,未能瞬間完成壓縮,無法應用於日常生活中。現今中央處理的速度已是當時的 100 倍以上,容許我們使用更繁複的演算法。
蘋果甚至在最新產品的硬件上,直接加入對 HEVC 及 HEIF 的支援,大大加快了影像編硬及解硬的過程,同時減少了圖像處理器的負擔,繼而減少電池的消耗。
## 一個檔案多張照片
一個 JPEG 檔只可存放一張照片,這是眾所周知的事實;但 HEIF 容許大家把多張照片存放於單一檔案內,像個照片容器。
把一系列相片放於單一檔案有什麼好處?許多手機都可拍攝 Live Photos 動態照片,即一序列數秒連續的照片。
如果把連續的照片放於單一的檔案既能使文件夾變得整齊也有助提升播放效率否則2 張 Live Photos 會演變成數十個 JPEG 檔;重播時,又要花時間尋找相關的照片。
## GIF 殺手
HEIF 能容納多張相片,已經有了 GIF 的最大優點。再者HEIF 像 GIF 一樣,支援透明背景,但 HEIF 的「通道位元深度」(Channel bit depth) 是 16 bits即 48 bits 的「像素位元深度」(Pixel bit depth) 遠大於 pixel bit depth 只有 8 bits 的 GIF 和 pixel bit depth只有 24 bits 的 JPEG。而相機的 channel bit depth 都只有 10 或 12 bitsHEIF 不會像 JPEG 那樣浪費相機的 bit depth可完整地保留顏色的層次。
如未清楚「通道位元深度」(Channel bit depth) 和「像素位元深度」(Pixel bit depth) 的分別,可參閱《[【攝影名詞】什麼是 Bit Depth (位元深度)Bits per Channel 和 Bits per Pixel 又有何分別?](https://www.imagejoy.com/article/655)》一文。
由此可見HEIF 不單是 JPEG 的終結者,還是個 GIF 殺手呢!
## 無損旋轉及裁砌
旋轉及裁砌相信是大家用得最多的相片編輯功能,而旋轉有可能對照片造成破壞,而裁砌肯定會對相片造成破壞。除非另存新檔,否則編輯後的照片是不可還原。 然而HEIF 天生已容許用家對照片進行無損的旋轉及裁砌,大家可把旋轉及裁砌後的照片完整無缺地還原。
## HEIF 的兼容性
目前為此iOS 是 HEIF 格式的最大的推行者。在 HEIF 未完全普及前,如把 HEIF 照片傳送到非 iOS 裝置或分享至不支援 HEIF 格式的 APPiOS 會把 HEIF 相片無聲無色地轉換成 JPEG 照片。
照片編輯方面Photoshp 和 Lightroom 已支援 HEIF / HEIC。 相信不久將來,大家都會把檔案輸出成 HEIF / HEIC (視乎其他廠商怎樣命名 HEIF 照片的副檔名),而非 JPEG。
## 參考
- [【攝影名詞】什麼是 HEIF/HEIC 照片格式?蘋果助攻的 JPEG 終結者 - 攝影入門教學 | ImageJoy](https://www.imagejoy.com/article/658)

View File

@@ -0,0 +1,81 @@
---
tags:
aliases:
date: 2025-01-13
time: 09:50:32
description:
---
## 讓玄關燈可以自動開關
希望大門打開的時候玄關燈可以自動亮起來,過一陣子之後自動熄滅省電。
### 準備
- 要買可控制的插座用來接目前的嵌燈
### 問題
- 小米目前的人體傳感器都缺貨,蝦皮上賣的都是陸版,等台灣再次撲貨
----------------------------------------
## 鐵皮屋
- 鐵皮屋門可以被偵測是否打開。
- 加裝攝影機。
- 濕度太高的話打開電風扇電風扇耗電34W還滿高的。
### 準備
- TP-Link 門窗偵測器
- TP-Link 網關
- TP-Link 攝影機
- TP-Link 溫濕度計
### Status
2025-01-15: 已在蝦皮購買,還沒到貨
----------------------------------------
## 弱電箱
Switch Hub 要接上 UPS
### 準備
- 把弱電箱裡面的電接出來給外面的UPS使用
- 把外面的UPS電送進去弱電箱
- RJ45 延長線接Switch Hub
- 打一個洞走光纖線
### Status
2025-01-15: 已在蝦皮購買零件,還沒到貨
----------------------------------------
## 太陽能儲電
準備一顆 100A 的鋰鐵電池。以備不時之需。
但是說實在的,我真的不知道可以在哪裡派上用場?
目前想到的就是可以用來幫戶外缸打氣。但是這樣又必須放上大大的太陽能板,感覺草坪那邊變得很擁擠。
----------------------------------------
## 門口攝影機
門口要裝攝影機,攝影機已經買了
### 問題
攝影機是白的,裝了很醜,要把它弄黑,但是很花時間
----------------------------------------
## 小孩房間油漆
### 準備
- [x] 匯訂金給吳先生 ✅ 2025-01-15
- [ ] 匯尾款給吳先生
----------------------------------------
## 小孩房間裝冷氣
- [ ] 聯絡冷氣📅 2025-02-03
----------------------------------------
## 小孩房間買家具
----------------------------------------
# 參考來源

176
00.01. 雜/MPPT.md Normal file
View File

@@ -0,0 +1,176 @@
---
tags:
aliases:
date: 2024-11-26
time: 10:25:17
description:
---
如果您正在尋找一種方法來優化您的 **[太陽系](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/solar-system-advantages-and-disadvantages)** 為了充分利用太陽能板,您可能需要考慮購買 MPPT 太陽能充電控制器。但什麼是 MPPT 充電控制器以及它如何運作?使用其中一種有什麼好處以及如何為您的系統選擇合適的一種?在這篇文章中,我們將回答這些問題以及更多問題,並幫助您了解為什麼 MPPT 充電控制器是任何太陽能愛好者的必備品。
![](https://cdn.shopify.com/s/files/1/0596/3713/7578/files/Solar_charge_controller_basic_configuration_480x480.png?v=1709023416)
## 介紹
### 什麼是太陽能充電控制器?為什麼它很重要?
A **[太陽能控制器](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/solar-charge-controller)** 是一種調節從太陽能板到電池組的電壓和電流的裝置。它可以防止電池過度充電,過度充電會損壞電池並縮短其使用壽命。它還可以防止電池過度放電,過度放電可能導致電池容量和效率損失。對於任何使用電池儲存太陽能電池板產生的多餘能量的太陽能係統來說,太陽能充電控制器都是必不可少的。
### MPPT 和 PWM 充電控制器有什麼不同?
太陽能充電控制器主要有兩種: [**最大功率點追蹤 (MPPT) 和脈寬調變 (PWM)**](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/mppt-vs-pwm-solar-charge-controller) 。它們之間的主要區別在於如何處理太陽能電池板電壓和電池電壓之間的不匹配。 PWM 充電控制器只是降低太陽能電池板電壓以匹配電池電壓這意味著它浪費了太陽能電池板產生的部分電力。另一方面MPPT 充電控制器將多餘的電壓轉換為更多的電流,這意味著它可以從太陽能板中提取最大功率。
### 使用 MPPT 充電控制器有哪些好處?
與使用 PWM 充電控制器相比,使用 MPPT 充電控制器有幾個優點。一些好處是:
- 更高的效率MPPT 充電控制器可以將太陽能係統的效率提高高達 30%,具體取決於條件。這意味著您可以從相同數量的太陽能電池板獲得更多的電力,或者使用更少的太陽能電池板來實現相同的電力輸出。
- 更大的靈活性MPPT 充電控制器可讓您使用電壓高於電池組的太陽能電池板,這為您提供了更多選擇。您還可以串聯太陽能電池板,從而減少接線和安裝成本。
- 更好的性能MPPT 充電控制器可以適應不斷變化的天氣和溫度條件,並始終找到太陽能板的最佳工作點。這意味著您可以 **[從太陽能板獲得更多電力](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/principles-of-solar-panels-and-how-they-work)** 即使陽光不強烈或溫度較低。
## MPPT 充電控制器的工作原理
### 最大功率點是多少MPPT 如何追蹤它?
最大功率點是太陽能板電流-電壓曲線上提供最大功率輸出的點。最大功率點會根據太陽輻照度(照射到太陽能板的陽光量)和太陽能板的溫度而變化。太陽輻照度越高、溫度越低,最大功率點越高。
MPPT 是一種持續監控太陽能板的電流和電壓並調整輸出以匹配最大功率點的技術。 MPPT 使用複雜的演算法來計算電流和電壓的最佳組合從而產生最高功率輸出。即使最大功率點因環境因素而變化MPPT 也能追蹤最大功率點。
### MPPT如何匹配電池電壓並增加電流
正如我們之前提到的,太陽能板電壓通常高於電池電壓,這意味著有一些多餘的電壓需要轉換成更多的電流。 MPPT 透過使用 DC-DC 轉換器來實現此目的DC-DC 轉換器是一種可以改變直流 (DC) 源的電壓和電流的設備。 DC-DC 轉換器可以升高(增加)或降低(減少)電壓,同時相反地改變電流。
MPPT 充電控制器使用 DC-DC 轉換器降低太陽能板電壓以匹配電池電壓同時按比例增加電流。例如如果太陽能板電壓為36V電流為5A電池電壓為12VMPPT充電控制器會將36V轉換為12V並將電流從5A增加到15A。這樣功率輸出保持不變36V x 5A = 12V x 15A = 180W但電流增加這意味著可以向電池輸送更多功率。
### MPPT 如何處理溫度和太陽輻照度的變化?
正如我們之前提到的太陽能電池板的最大功率點會根據溫度和太陽輻照度而變化。這意味著MPPT充電控制器需要不斷調整輸出以匹配最大功率點。 MPPT 透過使用回饋迴路來實現此目的,回饋迴路是一個將實際輸出與所需輸出進行比較並進行相應修正的系統。
MPPT充電控制器使用回饋迴路來測量太陽能板和電池的電流和電壓並將它們與最大功率點進行比較。如果實際輸出低於最大功率點MPPT充電控制器將增加輸出電壓並減少輸出電流直到達到最大功率點。如果實際輸出高於最大功率點MPPT充電控制器將降低輸出電壓並增加輸出電流直到達到最大功率點。這樣MPPT充電控制器就可以始終追蹤最大功率點並向電池提供最佳功率輸出。
## 如何選擇 MPPT 充電控制器
### 選擇MPPT充電控制器時要考慮的主要參數有哪些
當您購買 MPPT 充電控制器時,有幾個參數需要注意,例如:
- 最大輸入電壓:這是 MPPT 充電控制器可以處理太陽能板陣列的最高電壓。您需要確保 MPPT 充電控制器的最大輸入電壓高於太陽能板陣列的開路電壓 (Voc),也就是太陽能板未連接任何負載時的電壓。太陽能板的 Voc 隨溫度降低而增加,因此在計算 Voc 時需要考慮您所在位置可能的最冷溫度。
- 最大輸入電流:這是 MPPT 充電控制器可以處理來自太陽能板陣列的最大電流。您需要確保 MPPT 充電控制器的最大輸入電流高於太陽能板陣列的短路電流 (Isc),也就是充電時的電流。 **[太陽能板套件](https://zh-tw.shieldenchannel.com/collections/solar-panels)** 被短路。太陽能板的 Isc 隨著太陽輻照度的增加而增加,因此在計算 Isc 時,您需要考慮您所在位置可能最亮的陽光。
- 最大輸出功率:這是 MPPT 充電控制器可以提供給電池組的最高功率。您需要確保 MPPT 充電控制器的最大輸出功率高於負載的功率需求,也就是您想要與太陽能係統一起運行的所有裝置的總功耗。
- 電池相容性:這是 MPPT 充電控制器可以使用的電池組的類型和電壓。您需要確保MPPT充電控制器與您擁有的電池類型如鉛酸、鋰離子等和電池電壓如12V、24V、48V等相容或計劃用於您的太陽系。
### 如何確定適合您的太陽能係統的 MPPT 充電控制器的尺寸?
要確定適合您的太陽能係統的 MPPT 充電控制器的規格,您需要執行以下步驟:
- 第 1 步:計算太陽能板陣列的總功率輸出。您可以透過將每個太陽能電池板的額定功率 (W) 乘以陣列中太陽能電池板的數量來完成此操作。例如,如果您有 10 個太陽能板,每個太陽能板的額定功率為 100W則太陽能板陣列的總功率輸出為 10 x 100W = 1000W。
- 第 2 步:計算太陽能板陣列的最大輸入電壓。您可以透過將每個太陽能電池板的 Voc 乘以陣列中串聯的太陽能電池板數量來實現此目的。例如,如果您有 10 個太陽能電池板,每個太陽能板的 Voc 為 20V並且將它們串聯則太陽能電池板陣列的最大輸入電壓為 10 x 20V = 200V。
- 步驟 3計算太陽能板陣列的最大輸入電流。您可以透過將每個太陽能電池板的 Isc 乘以陣列中並聯的太陽能電池板的數量來實現此目的。例如,如果您有 10 個太陽能板,每個 Isc 為 5A並將它們並聯則太陽能板陣列的最大輸入電流為 10 x 5A = 50A。
- 步驟 4選擇具有比太陽能板陣列更高的最大輸入電壓、最大輸入電流和最大輸出功率的 MPPT 充電控制器。您還需要確保 MPPT 充電控制器與您的電池類型和電壓相容。例如,如果您有 1000W 太陽能板陣列,最大輸入電壓為 200V最大輸入電流為 50A並且您想使用 12V 鉛酸電池組,則可以選擇具有最大輸入電壓的 MPPT 充電控制器。輸入電壓250V 最大輸入電流60A最大輸出功率1200W相容於12V鉛酸電池。
### 如何比較不同品牌、型號的MPPT充電控制器
當您比較不同品牌和型號的 MPPT 充電控制器時,還需要考慮其他一些因素,例如:
- 轉換效率這是MPPT充電控制器可以從太陽能板陣列轉換到電池組的功率的百分比。轉換效率越高功率損耗越少輸送的功率越多。您可以透過查看規格或評論來比較不同 MPPT 充電控制器的轉換效率。
- 特性與功能這是MPPT充電控制器可以提供的附加特性和功能例如LCD顯示、遠端控制、資料記錄、溫度補償、負載控制等。功能和功能越多便利性和功能就越多。您可以透過查看手冊或網站來比較不同 MPPT 充電控制器的特性和功能。
- 價格和保固這是MPPT充電控制器的成本和保固。價格越低保固期越長價值和可靠性越高。您可以透過線上或線下商店比較不同 MPPT 充電控制器的價格和保固。
## 如何安裝並使用 MPPT 充電控制器
### 安裝MPPT充電控制器的基本步驟是什麼
要安裝 MPPT 充電控制器,您需要遵循以下基本步驟:
- 步驟1將MPPT充電控制器安裝在陰涼乾燥的地方遠離陽光直射、熱源和易燃材料。您還需要確保 MPPT 充電控制器周圍有足夠的通風和間隙,以利於散熱和空氣流通。
- 步驟 2依照極性和電壓規格將電池組連接到 MPPT 充電控制器。您需要使用適當的電纜、連接器和保險絲來連接電池,並確保電纜緊固。在將電池組連接到 MPPT 充電控制器之前,您還需要確保電池組已充滿電。
- 步驟 3依照極性和電壓規格將太陽能板陣列連接到 MPPT 充電控制器。您需要使用適當的電纜、連接器和二極體來連接太陽能板,並確保電纜緊固。在將太陽能板陣列連接到 MPPT 充電控制器之前,您還需要確保太陽能板陣列沒有暴露在陽光下。
- 步驟 4依照極性和電壓規格將負載連接到 MPPT 充電控制器。您需要使用適當的電纜、連接器和開關來連接負載,並確保電纜緊固。您還需要確保負載在連接到 MPPT 充電控制器之前已關閉。
- 步驟5開啟MPPT充電控制器並檢查狀態指示燈和顯示。您需要確保 MPPT 充電控制器正常運作並顯示正確的訊息。您還需要確保 MPPT 充電控制器沒有顯示任何錯誤代碼或警告。
### 安裝和使用MPPT充電控制器時需要遵循哪些安全注意事項
安裝和使用MPPT充電控制器時需要遵循以下安全注意事項
- 處理 MPPT 充電控制器、電池組、太陽能板陣列和負載時,請戴上防護手套、護目鏡和衣服。您需要避免觸電、短路、火災、爆炸和其他危險。
- 遵循 MPPT 充電控制器、電池組、太陽能板陣列和負載的說明和規格。您需要避免損壞、故障和保固無效。
- 請勿改裝、拆解或修復 MPPT 充電控制器、電池組、太陽能板陣列和負載。您需要避免受傷、故障和保固失效。
- 請勿將 MPPT 充電控制器、電池組、太陽能板陣列和負載暴露在水、灰塵、腐蝕、極端溫度或物理撞擊的環境中。您需要避免損壞、故障和保固失效。
### 如何監控 MPPT 充電控制器並排除故障?
若要監控 MPPT 充電控制器並對其進行故障排除,您需要執行以下操作:
- 定期監控MPPT充電控制器的狀態指示燈和顯示。您需要檢查輸入電壓、輸入電流、輸出電壓、輸出電流、輸出功率、電池電壓、電池充電狀態、電池溫度、負載狀態等資訊。您還需要檢查錯誤代碼和警告如果有
- 根據錯誤代碼和警告(如果有)對 MPPT 充電控制器進行故障排除。故障處理步驟和解決方案需要參考MPPT充電控制器的使用手冊或諮詢客服。如果需要保固服務您還需要聯絡MPPT充電控制器的製造商或經銷商。
## MPPT 充電控制器應用與範例
### MPPT 充電控制器有哪些常見應用?
MPPT充電控制器廣泛應用於需要太陽能發電的各種應用例如
- **[離網系統](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/what-is-an-off-grid-solar-system)**:這些系統未連接到電網,而是依靠太陽能和電池提供電力。 MPPT 充電控制器非常適合離網系統,因為它們可以最大限度地提高太陽能電池板的功率輸出並延長電池壽命。離網系統常用於偏遠地區,如鄉村、小木屋、房車、船隻等。
- **[併網系統](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/what-is-a-grid-tied-solar-system)**:這些系統連接到電網並使用太陽能來減少電費。 MPPT 充電控制器也適用於併網系統,因為它們可以提高太陽能板的效率並減少對電網的依賴。併網系統通常用於城市地區,例如家庭、辦公室、學校等。
- 混合系統:這些系統將太陽能與其他能源(例如風能、柴油或水力)結合。 MPPT 充電控制器也與混合系統相容,因為它們可以將太陽能與其他電源整合並優化能源管理。混合系統通常用於電網不穩定或不可靠的地區,例如島嶼、農場、度假村等。
### MPPT 充電控制器如何提高太陽能係統的效能和效率?
MPPT 充電控制器可以透過多種方式提高太陽能係統的效能和效率,例如:
- 增加功率輸出MPPT 充電控制器可將太陽能係統的功率輸出增加高達 30%,具體取決於條件。這意味著您可以從相同數量的太陽能電池板獲得更多的電力,或者使用更少的太陽能電池板來實現相同的電力輸出。
- 減少功率損耗MPPT 充電控制器可以透過最小化電壓降和電線電阻來減少太陽能係統的功率損耗。這意味著您可以使用更長、更細的電線來連接太陽能電池板和電池,從而節省金錢和空間。
- 增強電池保護MPPT 充電控制器可透過防止過度充電、過度放電和反極性來增強太陽能係統的電池保護。這意味著您可以延長電池壽命並避免電池損壞。
### MPPT 充電控制器的實際應用範例有哪些?
以下是 MPPT 充電控制器的一些實際應用範例:
- 肯亞的太陽能水泵系統:該系統使用 MPPT 充電控制器為水泵供電,為農村社區提供清潔水。 MPPT充電控制器可以追蹤太陽能板的最大功率點並調整輸出以匹配水泵。即使在多雲天氣下該系統每天也可泵送多達 40,000 公升水。
- 印度的太陽能路燈系統:該系統使用 MPPT 充電控制器為照亮村莊的 LED 路燈供電。 MPPT充電控制器可以優化太陽能板的功率輸出並調節電池的充電和放電。即使在雨季該系統每晚也能提供長達 12 小時的可靠且可持續的照明。
- 尼泊爾的太陽能冷凍系統:該系統使用 MPPT 充電控制器為保存疫苗和藥品的冰箱供電。 MPPT充電控制器可以最大限度地提高太陽能電池板的功率輸出並維持冰箱的溫度。即使在高海拔和低溫條件下該系統也能確保疫苗和藥物的安全有效。
## MPPT 充電控制器常見問題解答
### MPPT 充電控制器比 PWM 充電控制器更有效率多少?
MPPT 充電控制器通常比 [**PWM 充電控制器**](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/pwm-solar-charge-controller),因為它們可以將多餘的電壓轉換為更多的電流,而 PWM 充電控制器只是降低電壓以匹配電池。效率的確切差異取決於太陽能板電壓、電池電壓、溫度和太陽輻照度等條件。一般來說MPPT 充電控制器的效率比 PWM 充電控制器高 10% 到 30%。
### 我可以將 MPPT 充電控制器與任何類型的太陽能電池板和電池一起使用嗎?
MPPT 充電控制器與大多數類型的太陽能板和電池相容,只要它們符合 MPPT 充電控制器的規格和要求。在與任何太陽能板和電池一起使用之前,您需要檢查 MPPT 充電控制器的最大輸入電壓、最大輸入電流、最大輸出功率和電池相容性。安裝和使用MPPT充電控制器、太陽能板、電池時還需要遵循其說明和注意事項。
### MPPT 充電控制器的使用壽命有多長?其保固期為何?
MPPT 充電控制器採用耐用且優質的材料和組件製成,使用壽命長。 MPPT充電控制器的使用壽命取決於多種因素例如使用、維護和環境。一般來說MPPT充電控制器如果保養得宜的話可以使用10到15年甚至更長。 MPPT 充電控制器的保固期因製造商和型號而異。在購買和使用MPPT充電控制器之前您需要查看其保固政策和條款。
## 結論
在這篇文章中,我們解釋了 MPPT 充電控制器是什麼、它的工作原理、如何選擇、如何安裝和使用,以及它的一些應用和範例。我們希望這篇文章能幫助您了解為什麼 MPPT 充電控制器是任何太陽能愛好者的必備品。如果您有任何疑問或需要任何協助,請隨時與我們聯繫。我們很樂意協助您滿足太陽能需求。感謝您的閱讀,祝您有美好的一天!
# 參考來源
- [MPPT 太陽能充電控制器:它是什麼以及您為何需要它](https://zh-tw.shieldenchannel.com/blogs/portable-power-station/mppt-solar-charge-controller)

View File

@@ -0,0 +1 @@
- [理解梅尔频谱mel spectrogram_melspectrogram-CSDN博客](https://blog.csdn.net/bo17244504/article/details/124707265)

2
00.01. 雜/SSIM.md Normal file
View File

@@ -0,0 +1,2 @@
- [結構相似性 - 維基百科,自由的百科全書](https://zh.wikipedia.org/zh-tw/%E7%B5%90%E6%A7%8B%E7%9B%B8%E4%BC%BC%E6%80%A7)

View File

@@ -0,0 +1,3 @@
白噪音,是指一種功率譜密度為常數的隨機信號。這種信號在各個頻段上的功率譜密度是一樣的,由於白光是由各種頻率(顏色)的單色光混合而成,故叫白噪音,它類似於廢棄無線電或電視台的靜電聲音,由於同時發出均勻聲頻,人耳可以察覺到。
也就是什麼頻率都有聲音,全部混合在一起。

9
00.01. 雜/flask.canvas Normal file
View File

@@ -0,0 +1,9 @@
{
"nodes":[
{"id":"0cf3eb9ca4edb9e7","x":-183,"y":-31,"width":250,"height":60,"type":"text","text":"flask"},
{"id":"b9a009569e4d16d2","x":-183,"y":-160,"width":250,"height":60,"type":"text","text":"flask_login"},
{"id":"d1e07b3f6c214732","x":-380,"y":-300,"width":250,"height":60,"type":"text","text":"User instance\n`class User(UserMixin)`"},
{"id":"053c4bab0ff61e91","x":-380,"y":80,"width":460,"height":222,"type":"text","text":"- [第 8 章:用户认证 - Flask 入门教程](https://tutorial.helloflask.com/login/)\n- [第57天 Flask 用户登录 Flask-Login - 纯洁的微笑博客](https://www.ityouknow.com/python/2019/11/13/python-web-flask-login-057.html)\n- [第 25 天Flask登入系統 Flask-Login - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天](https://ithelp.ithome.com.tw/articles/10224408)"}
],
"edges":[]
}

View File

@@ -0,0 +1,15 @@
---
tags:
aliases:
date: 2024-06-06
time: 11:29:52
description:
---
- 是日本佛教特有的護法神之一。
- 守護佛法僧三寶,[[厭離]][[不淨觀|不淨]]。
- 形像一般是三面六臂或八面六臂三面像的頭上有5個小面
- 因治罰暴惡,所以呈現頭髪逆立的憤怒像,和密教的明王像共通。
- 因為是除去不淨、災難的神,所以江戶時代以降,被視為火神和竈神,多作為竈神祭祀,是廚房常見的神祇。
# 參考來源

13
00.01. 雜/不淨觀.md Normal file
View File

@@ -0,0 +1,13 @@
---
tags:
aliases:
date: 2024-06-06
time: 11:28:00
description:
---
- 佛教術語
- 為一種修行禪定的調伏心智的方法。
- 他們會藉由觀想人類身體的組成,以及人體在過世之後,成為屍體,並逐漸毀敗的過程,被認為是對治欲界貪欲特別有效的一種方式。也是五停心觀之一。
# 參考來源

3
00.01. 雜/亞甲藍.md Normal file
View File

@@ -0,0 +1,3 @@
又稱**亞甲基藍**、**次甲基藍**、**次甲藍**
其他俗稱舊稱有美藍、藍趴、甲烯藍、瑞士藍Swiss blue
[國際非專利藥品名稱](https://zh.wikipedia.org/wiki/%E5%9B%BD%E9%99%85%E9%9D%9E%E4%B8%93%E5%88%A9%E8%8D%AF%E5%93%81%E5%90%8D%E7%A7%B0 "國際非專利藥品名稱")INN為 methylthioninium chloride是一種[芳香](https://zh.wikipedia.org/wiki/%E8%8A%B3%E9%A6%99%E6%80%A7 "芳香性")[雜環化合物](https://zh.wikipedia.org/wiki/%E6%9D%82%E7%8E%AF%E5%8C%96%E5%90%88%E7%89%A9 "雜環化合物"),用作化學指示劑、染料、生物染色劑和藥物。

View File

@@ -0,0 +1,27 @@
```python
import numpy as np
n_fft=2048
ft = np.abs(librosa.stft(y[:n_fft], hop_length=n_fft+l))
plt.plot(ft)
plt.title('Spectrum')
plt.xlabel('Frequency Bin')
plt.ylabel('Amp1itude')
```
output e.g.:
![[Pasted image 20231212180917.png]]
這時候 Y 軸是 amplitude可以使用 `librosa.amplitude_to_db()` 來把 amplitude 轉為 db。
```python
import librosa.display
spec = np.abs(librosa.stft(y, hop_1ength=512))
spec = librosa.amplitude_to_db(spec, ref=np.max)
librosa.display.specshow(spec, sr=sr, x_axis='time', y_axis='log')
p1t.colorbar(format='%+2.0f dB')
plt.title( 'Spectrogram')
```
Output:
![[Pasted image 20231212181543.png]]

View File

@@ -0,0 +1,13 @@
由於人類會對低頻低音高的片段更感興趣,所以會對通過 FFT 變換得到的 Amplitude 和 Frequency。
[[Mel spectrogram]] 和 spectrogram 的差別就是 mel spectrogram 的頻率是 mel scale 變換後的頻率你可以想像把Spectrogram整體往下壓
```python
mel_spect = librosa.feature.melspectrogram(y=y, sr=sr, n fft=2048, hop_Iength=1024)
mel_spect = librosa.power_to_db(mel_spect, ref=np.max)
librosa.display.specshow(mel_spect, y_axis='mel', fmax=8000, x_axis='time')
plt.title('Mel Spectrogram')
p1t.colorbar(format='%+2.0f dB')
```
Output:
![[Pasted image 20231212181946.png]]

11
00.01. 雜/出離.md Normal file
View File

@@ -0,0 +1,11 @@
---
tags:
aliases:
date: 2024-06-06
time: 11:25:34
description:
---
意為對**欲樂**的棄絕,不為塵垢所染。
# 參考來源

12
00.01. 雜/厭離.md Normal file
View File

@@ -0,0 +1,12 @@
---
tags:
aliases:
date: 2024-06-06
time: 11:24:37
description:
---
- 佛教術語,「厭」指饜足、厭斥,「離」指[[出離]]、離欲。
- 即說對世間苦、集之知曉進而對世俗生活「厭倦」、不感興趣,而願意出離苦,求涅槃道。
# 參考來源

View File

@@ -0,0 +1,3 @@
###
父母疼囝長流水,無時停。
囝想父母樹尾風,有時陣。

View File

@@ -0,0 +1,44 @@
- 初聞不知曲中意,再聽已是曲中人。
- 讀書,就是要先將厚書讀薄,再將薄書讀厚。
- 種一棵樹最好的時候,一個是過去,一個是現在 - dead aid by Dambisa Moyo [^1] ^901833
- ![[20201224 - 寫作是最好的自我投資#^d7f87c|葉勝陶先生:「語言是有聲無形的文章,文章是有形無聲的語言。」]]
- ![[20201224 - 寫作是最好的自我投資#^fad99d|蘇格拉底:「未經審查的人生沒有價值。」]]
- ![[20201224 - 寫作是最好的自我投資#^fd93cf|「專業,二十一世紀你唯一的生存之道。」 - 大前研一]]
- ![[20201224 - 寫作是最好的自我投資#^a00bcf|「任何一個好產品都是聰明人用笨功夫做出來的」 - 咪蒙]]
- ![[20201224 - 寫作是最好的自我投資#^7e896d|「天才的唯一秘密,就在於刻意練習,用自己一套系統性的方法,不斷突破自己的邊界」 - 刻意練習,安德斯.艾瑞克森]]
- ![[20230206 - 卡片盒筆記#^f3d5d9|康德:「不成熟,是指若無他人的教導就不會運用自己的理解力...]]
- ![[20230206 - 卡片盒筆記#^1b06a6|我們並不是無法從經驗中學習,而是要再經驗發生之後快速獲得反饋(而且頻率密集),才能從自身的經驗中學到東西。]]
- ![[20230801 - 蘇格拉底哲學特快車#^8f104c]]
- ![[20250226 - 連結#^8caec9]]
---
1. 富人待人的共同點:明明知道這個人不行,但是他不提醒、不指點、不好為人師,即便是他的認知經驗都超過了對方,他也不會給對方的提建議。
因為無論你是指點、說教,都是需要消耗能量,情商最低的行為就是不停的講道理,智者戒口,愚者指點江山。能說服一個人的從來不是道理,而是南墻;能點醒一個人的從來不是說教,而是磨難。
2. 母弱出商賈,父強做史郎,族旺留原籍,家貧走他鄉。父母弱,就要學著做生意去改變命運;父母強,就要學會背靠大樹去入仕為宮。如果宗族很強,就利用家族名望在本地發展,那如果家境貧寒,就需要遠走他鄉去尋找出路了。
3. 放下助人情節,尊重他人命運。
社會的游戲規則,一層一層的難度係數和代價,都鎖死著不同的人,層層遞進。才會有這一句話:成年人,最終是被篩選出來的。你能賺得到多少錢,不是時間熬出來的,是社會中的他人把你篩選出來的。
4. 帶你賺錢的人,約你學習的人,和你談人生的人,和你聊理想的人,處處為你加油打氣,這才是貴人。故意在人多的時候訓你的人,你別往心里去,他是小人,在沒人的時候,才罵你跟你交心的人,千萬要記住,他是你的貴人。
真正的貴人,會鼓勵你,指引你,幫助你,開拓你的眼界,糾正你的格局,帶給你正能量。
5. 一句很有力量的話就是叫允許一切發生。
一切的發生,本身就是不可能去阻擋它的。有一句話叫境隨心變,所有不愉快的東西,你把它放在死亡面前你都可以包容,你都可以解釋,你就通了你就不會過得那麼委屈了。吃飯,睡覺才是天大的事,其他都可以放放。
6. 但凡有賺錢的好事,沒有人會免費教給你,要麼交學費,要麼對方真的願意幫你。不然做三年能成功的事情,可能就要花上五年。
7. 我很喜歡羅素的一句話:「屏蔽力」是一個人最頂級的能力。任何消耗你的人和事,多看一眼都是你的不對。
8. 近幾年明白了一個道理:就是人完全不需太懂事的,你只要形成自己的一套行事風格並發自內心不覺得有任何問題,那你身邊人就會自己調整,變換出一種能和你相處下去的模式和心態。
你不想想辦法,別人就想想辦法,非常神奇這叫“想辦法”守恒。
9. 無論是什麼關系,提供不了情緒價值,給予不了經濟支持,給不了正面陪伴,三點不佔一樣,捨棄才是明智之舉。
10. 告訴你,只要你擔心別人會怎麼看你,他們就能奴役你;只有你再也不從自身之外尋求肯定,那你才能真正成為自己的主人。
---
[^1] [“種一棵樹最好的時間是十年前,其次是現在”出自哪裡?](https//zhidao.baidu.com/question/652202353537726525.html)

View File

@@ -0,0 +1,6 @@
- 這是對個人成長阻礙最大的心態。害怕、逃避反饋的人,擔心反饋可能會傷害到自己的好形象,或許短時間內會覺得自己很優異,但是很快會在真正的表線上落後。諷刺的是,很多聰明、天賦異稟的學生,因為過去自身天賦異稟受到讚美,而不是因為自己的表現兒受到讚揚,因此多半只想完整保持住這個形象,不想讓自己接觸到新的挑戰,不想從失敗中學習。[^1]
[^1]: 卡片盒筆記P137。

View File

@@ -0,0 +1,53 @@
---
tags:
aliases:
date: 2025-01-06
time: 09:58:20
description:
---
被演算法推到這部舊影片,結果竟然上癮地看了兩遍。真是相見恨晚。世界上有什麼東西不能用 AI 摘要的呢?也許這部影片就是。
Patrick Winston 是美國電腦科學家,在 MIT 任教近 50 年,也是 AI 領域的權威之一。
除了 AI 研究,他最有名的是一堂在 MIT 開設超過 40 年的名課《如何表達》How to Speak。這堂課年年爆滿甚至必須限制人數。
YouTube 上有他 2018 年講課的完整錄影。令人遺憾的是 Winston 於隔年過世。影片連結放留言。
嚴格來說,整堂六十分鐘的課並沒有傳授什麼「魔法」。然而,真正的魔法是這六十分鐘過得極快,才知道原來他在每一個環節都用上了自己示範的技巧。
以下是比較有印象的重點,但仍高度推薦直接看全程影片,真的是享受。
--------------------------------------------------------------------------------
## 如何開場
1. 不要以笑話開場。
2. 給聽眾一個聽完後的承諾。
## 如何啟發別人
1. 重要的事情要講三遍。
2. 明確劃分自己的內容與他人的不同。
3. 適時回頭帶一下重點,拉回注意力。
## 技術性細節
1. 挑合適的時間,例如早上 11 點。
2. 地點應該燈光充足。
3. 粉筆與黑板很好用。
4. 手不要插在口袋,也不要放在背後。
5. 不要用雷射筆。
6. 字絕對不能多。
7. 字體最小為 40 級,既清楚也能避免太多。
8. 投影片是用來展示概念,不是用來念或看的。
9. 適當使用道具有助於聽眾記憶重點。
10. 不要找專家排練,因為怎樣他都聽得懂。
## 如何激勵聽眾
1. 只要你展現出熱情,聽眾就會感到被啟發。
2. 給聽眾一個故事,並帶領他們提問、甚至分析。
## 如何說服聽眾
1. 為了工作:分享願景,並說明解決問題的步驟。
2. 為了有名:透過理念、故事、口號來打造個人品牌。
## 如何結束演講
1. 絕不說「謝謝」,這很像台下的人只是基於禮貌才聽。
2. 可以用行動號召作結。
3. 或是你終於可以講個笑話。
# 參考來源
- [Manny YH Li - 被演算法推到這部舊影片,結果竟然上癮地看了兩遍。真是相見恨晚。世界上有什麼東西不能用 AI... | Facebook](https://www.facebook.com/mannyyhl/posts/pfbid0EycLQ9VFmJFwW9qr5SAhNMp8J86Nb59B5sCK55G2juNpV1S1xFbBxv22C9tCEuWCl)

View File

@@ -0,0 +1 @@
- 今天沒有時間,所以信的內容變得很長

View File

@@ -0,0 +1,15 @@
---
tags:
aliases:
date: 2024-06-20
time: 00:02:21
description:
---
## 飼料
- [[飼料]]
## 問題
- 胃扭轉
# 參考來源

View File

@@ -0,0 +1,13 @@
---
tags:
aliases:
date: 2023-04-25
time: 21:48:33
description:
---
積極尋求並歡迎各種反饋,不論正面還是負面,是長期獲得成功(還有快樂)的一個重要因素[^1]。
[^1]: 卡片盒筆記P137。

17
00.01. 雜/挖礦.canvas Normal file
View File

@@ -0,0 +1,17 @@
{
"nodes":[
{"id":"9d207efca8e39cd4","type":"text","text":"Zephyr","x":-407,"y":58,"width":125,"height":60,"color":"3"},
{"id":"cbead737c83840bd","type":"text","text":"## 錢包\n[zephyrprotocol](https://wallet.zephyrprotocol.com/)","x":-441,"y":-180,"width":194,"height":90},
{"id":"e96c80e8aad5e6bc","type":"text","text":"## 教學\n[# ZEPH挖矿教程 CPU挖矿 全流程讲解 算力低解决方法](https://www.youtube.com/watch?v=Ro8XNMlAtrM)","x":-554,"y":262,"width":421,"height":92},
{"id":"c06fb876aad6755a","type":"text","text":"## 挖礦工具\n- [xmrig](https://github.com/xmrig/xmrig)\n\n### Set donate to 0\n[[Build xmrig with Visual Studio 2022]]","x":0,"y":-260,"width":355,"height":185},
{"id":"fa19a71efffddcb1","type":"text","text":"## 輔助工具\n### 選礦池\n[zephyr.miningocean.org](https://zephyr.miningocean.org/worker_stats)\n\n### 查CPU/GPU 算力\n- [XMRig](https://xmrig.com/benchmark)\n- https://www.hashrate.no/cpus\n\n### 算收益\n[Zephyr 挖矿收益计算器](https://minerstat.com/coin/ZEPH?lang=zh)","x":0,"y":-60,"width":355,"height":357},
{"id":"9bb4b7969d5cf3e8","type":"text","text":"## 設定\n- [Windows 11 算力最大化](https://www.youtube.com/watch?v=vUbjxpBc9N8)","x":0,"y":310,"width":355,"height":89}
],
"edges":[
{"id":"fda9bf543a8846de","fromNode":"9d207efca8e39cd4","fromSide":"top","toNode":"cbead737c83840bd","toSide":"bottom"},
{"id":"dea21c3da2418bf6","fromNode":"9d207efca8e39cd4","fromSide":"right","toNode":"c06fb876aad6755a","toSide":"left"},
{"id":"108524e502ec98a1","fromNode":"9d207efca8e39cd4","fromSide":"right","toNode":"fa19a71efffddcb1","toSide":"left"},
{"id":"c272278ac173632a","fromNode":"9d207efca8e39cd4","fromSide":"bottom","toNode":"e96c80e8aad5e6bc","toSide":"top"},
{"id":"b3999d558fa417e8","fromNode":"9d207efca8e39cd4","fromSide":"right","toNode":"9bb4b7969d5cf3e8","toSide":"left"}
]
}

View File

@@ -0,0 +1 @@
我們對現在的想法與未來的想法大不相同。

6
00.01. 雜/皮質醇.md Normal file
View File

@@ -0,0 +1,6 @@
皮質醇會直接妨礙認知與判斷能力。
壓力會引發皮質醇釋放影響大腦的幾個區域例如前額葉皮質區、杏仁核amygdala、海馬體hippocampus等等。
前額葉皮質區對於抑制衝動極為關鍵。 [^1]
[^1]: [[20230418 - 窮人的經濟學]] p.178

View File

@@ -0,0 +1 @@
- [我的個人知識管理系統 - Pin 起來!](https://pinchlime.com/blog/my-personal-knowledge-management-system-2023/)

2
00.01. 雜/稼動率.md Normal file
View File

@@ -0,0 +1,2 @@
亦稱為「產能利用率」,是用來衡量機器設備使用效率的重要指標之一,簡單來說就是用機器設備可以運轉的時間(即負荷時間)與扣除掉因保養、維修、假日等的停機時間之比率,其計算公式如後:稼動率=(負荷時間-停機時間)/ 負荷時間 。

View File

@@ -0,0 +1,37 @@
{
"nodes":[
{"id":"4bc28156ba914925","type":"text","text":"成功","x":-127,"y":-32,"width":107,"height":60},
{"id":"9bf6e96eb80b5ef9","type":"text","text":"熱情","x":-127,"y":100,"width":107,"height":60},
{"id":"c00f7241e4fb1e59","type":"text","text":"自律","x":100,"y":-32,"width":93,"height":60},
{"id":"2949ff9fb5621558","type":"text","text":"願景","x":-127,"y":-180,"width":107,"height":60},
{"id":"0964b9a7120a94b2","type":"text","text":"放縱","x":300,"y":-32,"width":108,"height":60},
{"id":"b642b18083f2f181","type":"text","text":"犧牲重要的東西以換取一時的歡樂","x":480,"y":-32,"width":212,"height":60},
{"id":"eb9431052053dfa5","type":"text","text":"腦海中看到的未來","x":-198,"y":-320,"width":250,"height":60},
{"id":"c8189efaab564e7a","type":"text","text":"沒有熱情\n就容易跟著流行走而迷失自己","x":-198,"y":260,"width":250,"height":60},
{"id":"bf816d5dd96ac24c","type":"text","text":"判斷是非的道德感","x":-680,"y":-32,"width":250,"height":60},
{"id":"36a3913993b8a464","type":"text","text":"良知","x":-360,"y":-32,"width":100,"height":60},
{"id":"1ad4e8eefb07a55f","type":"text","text":"## 不道德\n1. 不勞而獲的財富\n2. 缺乏良知的歡樂\n3. 沒有品味的知識\n4. 缺乏道德的商業\n5. 不人道的科學\n6. 沒有犧牲的崇拜\n7. 無原則的政治","x":-1100,"y":-118,"width":250,"height":232},
{"id":"5568235e8c0f93fd","type":"text","text":"誠信","x":-680,"y":-148,"width":140,"height":60},
{"id":"fe97a4207f8cc0d1","type":"text","text":"平和","x":-680,"y":-210,"width":140,"height":60},
{"id":"e9c988d9c758436c","type":"text","text":"| 全人 | 四項需求 | 四項才能 | 四項品質 | 心聲 |\n|:------:|:-------:|:-------------:|:------:|:------:|\n| 身體 | 生活 | 身體能力PQ | 自律 | 需求<br>(看到並滿足需求) |\n| 心智 | 學習 | 智力IQ | 願景 | 天賦才能<br>(自律的關注焦點)|\n| 情感 | 愛 | 情感力EQ | 熱情 | 熱情<br>(熱衷於某事)|\n| 心靈 | 發揮影響力| 精神力SQ | 良知 | 良知<br>(做正確的事)|","x":-1100,"y":-524,"width":529,"height":264}
],
"edges":[
{"id":"241af6fdb0c62886","fromNode":"2949ff9fb5621558","fromSide":"right","toNode":"c00f7241e4fb1e59","toSide":"top","fromEnd":"arrow","label":"需要"},
{"id":"2a2505cd34afee38","fromNode":"c00f7241e4fb1e59","fromSide":"right","toNode":"0964b9a7120a94b2","toSide":"left","fromEnd":"arrow","label":"相反面"},
{"id":"a8030fccc63e991d","fromNode":"0964b9a7120a94b2","fromSide":"right","toNode":"b642b18083f2f181","toSide":"left"},
{"id":"50ef3c5170178c34","fromNode":"2949ff9fb5621558","fromSide":"top","toNode":"eb9431052053dfa5","toSide":"bottom"},
{"id":"299dd8caee4c658c","fromNode":"4bc28156ba914925","fromSide":"top","toNode":"2949ff9fb5621558","toSide":"bottom"},
{"id":"b1c9d9de2be64b3e","fromNode":"4bc28156ba914925","fromSide":"left","toNode":"36a3913993b8a464","toSide":"right"},
{"id":"ec48f8a15eec067c","fromNode":"4bc28156ba914925","fromSide":"bottom","toNode":"9bf6e96eb80b5ef9","toSide":"top"},
{"id":"9be33fefcef78a91","fromNode":"4bc28156ba914925","fromSide":"right","toNode":"c00f7241e4fb1e59","toSide":"left"},
{"id":"4d7452f97359f74f","fromNode":"9bf6e96eb80b5ef9","fromSide":"right","toNode":"c00f7241e4fb1e59","toSide":"bottom","label":"提供動力"},
{"id":"bb333dafe2c3d8a3","fromNode":"9bf6e96eb80b5ef9","fromSide":"bottom","toNode":"c8189efaab564e7a","toSide":"top","fromEnd":"arrow","label":"相反面"},
{"id":"91d81e51a4933de7","fromNode":"36a3913993b8a464","fromSide":"left","toNode":"bf816d5dd96ac24c","toSide":"right"},
{"id":"235c633e9c020893","fromNode":"36a3913993b8a464","fromSide":"top","toNode":"9bf6e96eb80b5ef9","toSide":"top","label":"指導力量"},
{"id":"70558ed8171bb110","fromNode":"bf816d5dd96ac24c","fromSide":"left","toNode":"1ad4e8eefb07a55f","toSide":"right","label":"相反面"},
{"id":"0a7657886c552afc","fromNode":"bf816d5dd96ac24c","fromSide":"top","toNode":"5568235e8c0f93fd","toSide":"right"},
{"id":"c018413f49ed30b5","fromNode":"bf816d5dd96ac24c","fromSide":"top","toNode":"fe97a4207f8cc0d1","toSide":"right"},
{"id":"3e1a9dd13bcb3869","fromNode":"36a3913993b8a464","fromSide":"top","toNode":"2949ff9fb5621558","toSide":"bottom","label":"指導力量"},
{"id":"4acfcf6f0a4d90aa","fromNode":"36a3913993b8a464","fromSide":"top","toNode":"c00f7241e4fb1e59","toSide":"left","label":"指導力量"}
]
}

13
00.01. 雜/飼料.md Normal file
View File

@@ -0,0 +1,13 @@
---
tags:
aliases:
date: 2024-06-20
time: 00:02:26
description:
---
- https://www.mobile01.com/topicdetail.php?f=290&t=3688553
- 因我現在有飼養一隻10個月大的狼犬在8個月前我都給牠吃皇家大型幼犬飼料牠身上就一直有體臭因養在室內所以每周都必須洗澡但是後來換了Prairie的羊肉飼料牠的體臭就消失了這個月我又買了皇家的大型犬成犬飼料牠的味道又來了。冒昧問你你養德國牧羊犬的目的
- [Prairie大草原 紐西蘭鮮羊肉全犬配方4.5磅 | Yahoo奇摩購物中心](https://tw.buy.yahoo.com/gdsale/Prairie%E5%A4%A7%E8%8D%89%E5%8E%9F-%E7%B4%90%E8%A5%BF%E8%98%AD%E9%AE%AE%E7%BE%8A%E8%82%89%E5%85%A8%E7%8A%AC%E9%85%8D%E6%96%B94-5%E7%A3%85-5663144.html)
# 參考來源

27
00.01. 雜/魚病.canvas Normal file
View File

@@ -0,0 +1,27 @@
{
"nodes":[
{"id":"335a89dbc334dd1b","type":"text","text":"## 魚體表會出現白色且界線明顯的斑塊\n看起來像是體表顏色稍微變淡。大多從頭、鰭基部或尾柄開始發生。隨著時間過去白斑周遭會有一圈紅色的發炎帶白色部份的皮膚可能被完全侵蝕而露出肌肉的紋理。","x":-695,"y":-300,"width":272,"height":200},
{"id":"7f4edf44d2c506fb","type":"text","text":"## 魚會變得很喘\n魚鰓上會出現黃白色的黏液且鰓會出現末端局部白化的現象。","x":-389,"y":-300,"width":250,"height":200},
{"id":"ceff7f34f6a04b7a","type":"text","text":"## 游泳歪斜,或是很常停在定點休息\n感覺很虛弱","x":-109,"y":-300,"width":250,"height":200},
{"id":"21e3e28e19400ffd","x":164,"y":-300,"width":250,"height":200,"type":"text","text":"## 魚鰭上的膜會快速消失,只留下鰭條"},
{"id":"615a1fa0b7c50bc7","x":440,"y":-300,"width":250,"height":200,"type":"text","text":"## 外觀看起來很像魚的嘴巴爛掉\n會長出一圈白色的物質。嚴重的時候會併發水黴菌感染。可能會影響魚隻的食慾。"},
{"id":"918d332bb59c9662","type":"text","text":"柱狀病","x":-36,"y":120,"width":104,"height":60},
{"id":"2b32355f7e5170ea","x":-389,"y":60,"width":250,"height":180,"type":"text","text":"## 超急性感染\n\n魚隻沒有特別的症狀但在感染12小時~24小時後會突然大量暴斃。"},
{"id":"f0af8b3344f253bd","x":164,"y":60,"width":250,"height":180,"type":"text","text":"## 慢性感染:\n\n魚隻外觀沒有明顯的異常初期也沒有症狀但其腎臟會受到感染而逐漸喪失功能。而讓魚隻出現腹水的現象最後因臟器衰竭而死亡。從感染到死亡從數個禮拜到數個月不等。"},
{"id":"844caecc04a86213","x":-135,"y":320,"width":303,"height":97,"type":"text","text":"- [柱狀病 | 塔魚手札](https://www.towerfish.tw/information_detail.php?id=54)\n- [柱狀病症狀及治療方式](https://fish.tsumii.com/%E6%9F%B1%E7%8B%80%E7%97%85%E7%97%87%E7%8B%80%E5%8F%8A%E6%B2%BB%E7%99%82%E6%96%B9%E5%BC%8F/)"},
{"id":"2f08b5e1af3b2a95","x":-36,"y":580,"width":104,"height":60,"type":"text","text":"水霉"},
{"id":"f7fe65e825209b8a","x":-36,"y":820,"width":104,"height":60,"type":"text","text":"腸炎"},
{"id":"1e3828a7ea29178e","x":-440,"y":580,"width":120,"height":60,"type":"text","text":"甲基藍"}
],
"edges":[
{"id":"ea7bfbba50be04ae","fromNode":"918d332bb59c9662","fromSide":"left","toNode":"2b32355f7e5170ea","toSide":"right"},
{"id":"08cb33e6b4992dbb","fromNode":"918d332bb59c9662","fromSide":"right","toNode":"f0af8b3344f253bd","toSide":"left"},
{"id":"f051b02fb8da231f","fromNode":"918d332bb59c9662","fromSide":"top","toNode":"335a89dbc334dd1b","toSide":"bottom"},
{"id":"13b1d002e0a882b0","fromNode":"918d332bb59c9662","fromSide":"top","toNode":"7f4edf44d2c506fb","toSide":"bottom"},
{"id":"1724fab53b8ae982","fromNode":"918d332bb59c9662","fromSide":"top","toNode":"ceff7f34f6a04b7a","toSide":"bottom"},
{"id":"dc0d96eddf6e175f","fromNode":"918d332bb59c9662","fromSide":"top","toNode":"21e3e28e19400ffd","toSide":"bottom"},
{"id":"eab89ad662b34431","fromNode":"918d332bb59c9662","fromSide":"top","toNode":"615a1fa0b7c50bc7","toSide":"bottom"},
{"id":"f5ab2deee8bd0e45","fromNode":"918d332bb59c9662","fromSide":"bottom","toNode":"844caecc04a86213","toSide":"top"},
{"id":"9ba59b2d6c8d8b89","fromNode":"2f08b5e1af3b2a95","fromSide":"left","toNode":"1e3828a7ea29178e","toSide":"right"}
]
}

View File

@@ -0,0 +1,39 @@
---
tags: 魚藥, Levamisole, 左旋咪唑, 左美索, 左美素
aliases:
date: 2024-10-09
time: 11:01:28
description:
---
廣效的抗寄生蟲藥物,它對人及動物的大部分線蟲感染均有廣效的驅蟲效力,所以被廣泛的應用在人類及動物。
# 主治
- 七彩消瘦,變黑,肚子扁扁
- 駝型線蟲
- 蟯蟲
- 腹水
- 鰷蟲
- 蛔蟲
- 鉤蟲
- 糞圓蟲
- 線蟲
- 鑽頭蟲
- 蟲卵(可殺死蟲卵)
# 作用機制
作用於線蟲可使蟲體麻痺,當動物服用 Levamisole 後若蟲體快速隨糞便被排出在糞便中尚可見活的蟲體而若經過較長的時間才排出則蟲體分解而無法於糞便中見到。Levamisole 是一種快速作用的驅蟲劑作用於線蟲神經節在和線蟲接觸的數秒鐘內麻痺蟲體肌肉。蟲體無法維持正常位置通常在24小時內被蠕動作用排出體外。
# 使用方法
## 浸泡
每一百公升的水使用0.2公克2PPM重症藥量加倍藥效維持四十八小時故可於四十八小時後再下藥再添加換水量的藥量即可。
最高劑量 10ppm。
## 口服
可直接添加在魚食中,每 100 公克魚食可混合本品 1 公克1000mg餵食連續 5-7 天。
# 注意事項
當遇上魚體皮肉出現肉眼可見的絲蟲一類的頑強寄生蟲,一般驅蟲藥如 [[魚藥 - Mebendazole美鞭達唑]],褔馬林、銅藥都未必可以解決。因為 [[魚藥 - Mebendazole美鞭達唑]] 不能被腸道吸收進入血液只能對腸道及體外蟲體發揮作用但生長於皮肉中的寄生蟲就不能對付。Levamisole 可以經腸道吸收,經血液送到全組織,針對組織中的寄生蟲,但缺點是毒性較強。
# 資料收集
- [三種體內藥物 | Facebook](https://www.facebook.com/legacy/notes/250504688381870/)

View File

@@ -0,0 +1,52 @@
---
tags: 魚藥, Mebendazole, 美鞭達唑
aliases:
date: 2024-10-09
time: 11:00:47
description:
---
# 主治
- 鞭蟲
- 蟯蟲
- 蛔蟲
- 鉤蟲
- 絲蟲
- 對蟲卵亦有殺滅效果
# 作用機制
經由阻斷 [[ATP]] 的合成來達到殺死寄生蟲的目的。基本上 [[ATP]] 乃是生物維持活動的能源、養分,若使寄生蟲無法吸收養分,便會逐漸消耗而死亡,最後隨腸道運動排出。
安全性高、但作用慢。且必須週期重複使用才能斷根(在水族而言、是必須同時大量換水移除蟲體)。
# 副作用
1. 本藥品屬直接殺蟲,故不需要併服瀉劑。
2. 可抑制蟲卵形成幼蟲,故對體內之寄生蟲能發揮徹底的清除之效。
3. 因不易被吸收,故不會引起全身性之毒性。
4. 僅會抑制蟲體對葡萄糖之攝取,對人體則無作用,所以即使服用高劑量也不會影響血糖濃度。
5. 對鞭蟲WHIPWORM、蛔蟲YOUNDWORM、蟯蟲PINWORM、美國鉤蟲AMERICAN HOOKWORM、普通的鉤蟲(COMMONHOOKWORM)、絲蟲THERADWORM皆有顯著的作用尤其是鞭蟲。故不僅對單寄生蟲感染有卓效對混合感染更是有實質的價值。
# 使用方法
## 浸泡
### 去除魚隻體表寄生蟲
- 每一百公升的水使用0.5公克,藥效維持四十八小時,故可於四十八小時後再下藥,再添加換水量的藥量即可。(重症藥量加倍)
- ※ 使用本劑水溫提高到32~34度 。
- 必須要先用甲酸或DMSO將藥物完全溶解後再倒入缸中治療。處理的時間為24小時24個小時之後則必須要將水的分批的換掉減少環境中的藥劑量。雖然將魚移出治療缸治療的話就不用擔心換水的問題但是存留在缸中的蟲卵就無法被殺滅可能會需要反覆的給藥。因此可以選擇在原缸中治療但花較多的心力去換水或者是移出治療但每個禮拜治療一次重複三次。但必須要注意反覆的治療可能會出現抗藥性。
### 去除底土的無脊椎生物
- 不需要用醋酸將藥物溶解,直接磨粉後加水均勻的撒在底土上或是埋入底土內即可,而且需要的藥量並非以水體積下去計算,要以底土的體積下去計算。因為底土上的藥物添加後並不會特別的移除,如果以水體積下去計算可能會導致藥物劑量過高,造成魚隻的死亡。
## 口服
- 口服給藥每公斤魚體重給予50mg的藥物
- 口服給藥主要用來去除體內的線蟲與絛蟲感染每天餵食一次藥餌連續餵食3~5天。藥餌的製做為先將飼料用水稍微的沾濕把藥丸磨成藥粉後直接將藥粉灑到飼料上攪拌均勻稍微晾乾後即可以餵食。餵食時使用的飼料量為平常給的飼料量的三分之一到一半左右即可。另外必須要確定所有的魚隻都有吃到飼料必要時須將魚隻隔離分開餵食。
# 注意事項
- 如果藥物處理的時間不夠久,會讓蟲體有恢復的機會而失效,但如果處理太久,讓魚隻接觸過久的藥物,也會產生毒性。因此使用此類藥物時,藥物處理的時間非常重要。
- 由於藥物是干擾蟲體的能量代謝因此大多數的蟲體要在藥物處理完後1~2天才會出現明顯的死亡。不過當魚隻的消化道內如果被比較多寄生蟲感染時使用此類藥物後就會造成大量的白便這些糞便內可能含有部分還沒死亡的寄生蟲如果健康的魚隻食入會有感染的風險所以建議要快速的移除這些異常的糞便。
- 對胎兒有致畸胎效果:在綿羊的實驗上已經證實對懷孕早期的胎兒具有致畸胎效果。雖然目前在人和魚身上還沒有報告,但在對種魚使用此藥的時候還是要特別的小心。另外也盡量不要讓孕婦接觸此藥。
- 不同魚種的耐受性不同:部分鯉科與脂鯉科魚類(大部分的燈科魚類都屬於這兩類的魚隻)還有無鱗魚(例如:鯰魚、鰻魚)對mebendazole較為敏感因此盡量不要使用此類藥物進行治療如果需要使用則要使用最低劑量且一定要特別注意藥物殘留的問題。
- 對軟體動物與蝦類具有殺滅能力軟體生物也會被此類藥物殺死使用劑量與驅蟲劑量相同只要浸泡即能殺滅。蝦子對mebendazole的抗性比軟體動物還要強一點不過只要劑量稍微超標也可能會死亡。
- 不溶於水和酒精只能用甲酸或DMSO溶
# 資料收集
- [水族吸蟲藥-美鞭達唑的使用 | 塔魚手札](https://www.towerfish.tw/information_detail.php?id=11)
- [三種體內藥物 | Facebook](https://www.facebook.com/legacy/notes/250504688381870/)

View File

@@ -0,0 +1,25 @@
---
tags: 魚藥, Metronidazole, 硝基嘧唑乙醇
aliases:
date: 2024-10-09
time: 11:01:09
description:
---
# 主治
- 鞭毛蟲
- 六鞭毛蟲
- 蟯蟲
- 頭槽滌蟲
- 頭洞病
- 鰓病
- 用於抗原蟲類、但作用機轉尚不明朗、而對於厭氧菌感染相當有效. 可是有肝毒性(且可能因個體而有過敏反應)。
# 作用機制
本劑乃將魚體內寄生蟲與體內寄生蟲,做一次清理。
# 使用方法
每一百公升的水使用0.5公克(重症藥量加倍),藥效維持四十八小時,故可於四十八小時後再下藥,再添加換水量的藥量即可。
# 資料收集
- [三種體內藥物 | Facebook](https://www.facebook.com/legacy/notes/250504688381870/)

View File

@@ -0,0 +1,36 @@
---
tags:
aliases:
date: 2025-02-01
time: 17:04:50
description:
---
# 2025-03-06
- [x] Try [uv](https://dev.to/codemee/shi-yong-uv-guan-li-python-huan-jing-53hg) ✅ 2025-03-28
# 2025-02-23
- [ ] Try [loguru](https://github.com/Delgan/loguru)
# 2025-02-02
- [ ] 搞懂 CSS
- [ ] 試試 [Tocas UI](https://tocas-ui.com/5.0/zh-tw/index.html)
- [ ] GCNAT?
- [ ] STUN?
- [ ] full cone nat?
# 2025-02-01
- [ ] 🛫 2025-02-01 [VSCODE] Optimise Ruff
- [Python 開發Ruff Linter、Formatter 介紹 + 設定教學 - Code and Me](https://blog.kyomind.tw/ruff/)
- [大一统的 Ruff: All-in-One Linter & Formatter for Python | DavidZ's Blog](https://blog.davidz.cn/post/aio-ruff)
- [【Python 軍火庫🧨 - Ruff】更加豐富強大的Python Linter方格子 vocus](https://vocus.cc/article/65390855fd89780001fe7001)
- [Tutorial | Ruff](https://docs.astral.sh/ruff/tutorial/#getting-started)
- [x] 🛫 2025-02-01 [VSCODE] 讓 Python 更好用 ✅ 2025-02-20
- [x] 可以 inline debug ✅ 2025-02-20
- [x] 自動寫 commit message ✅ 2025-02-19
- [x] 🛫 2025-02-01 [VSCODE] 更好看的 UI ✅ 2025-02-20
- [ ] 🛫 2025-02-01 [VSCODE] 設定 pre-commit
- [Python 開發pre-commit 設定 Git Hooks 教學 - Code and Me](https://blog.kyomind.tw/pre-commit/#%E7%82%BA%E4%BB%80%E9%BA%BC%E8%A6%81%E4%BD%BF%E7%94%A8-pre-commit%EF%BC%9F)
# 參考來源

View File

@@ -0,0 +1,14 @@
---
tags: logitech, autoserver, kirby
aliases:
date: 2024-05-28
time: 14:20:24
description:
---
- 要加入 templates、static 與其他的靜態檔案
- DB 位置不能用相對路徑,必須[[Determine Python is running as a script or as a frozen exe]],然後更新路徑
- 要把 `flask db upgrade` 也變成 EXE 檔。
# 參考來源

View File

@@ -0,0 +1,23 @@
---
tags:
aliases:
date: 2025-01-03
time: 09:56:52
description:
---
時間09:56:52
---
# 今日發生什麼事?
最近的想法有點多:
1. 讓玄關的燈可以自動打開關閉
- 這個還沒做,需要買一個小米的智慧插座
- 可是小米的人身傳感器一直缺貨
# 有什麼想法?
# 相對應的行動是什麼?

View File

@@ -0,0 +1,28 @@
---
tags:
aliases:
date: 2025-01-13
time: 09:49:26
description:
---
時間09:49:26
---
# 今日發生什麼事?
延續上次,最近的想法有點多,我把它們紀錄在[[Home Project]]裡面:
![[Home Project#讓玄關燈可以自動開關]]
![[Home Project#鐵皮屋]]
![[Home Project#弱電箱]]
![[Home Project#門口攝影機]]
![[Home Project#太陽能儲電]]
![[Home Project#小孩房間油漆]]
![[Home Project#小孩房間裝冷氣]]
![[Home Project#小孩房間買家具]]
# 有什麼想法?
# 相對應的行動是什麼?

View File

@@ -0,0 +1,25 @@
---
tags:
aliases:
date: 2025-01-14
time: 09:19:56
description:
---
時間09:19:56
---
# 今日發生什麼事?
[[Home Project]]
一直在考慮須不需要裝一顆鋰鐵電池來用,說真的在平常供電穩定的時候裝一顆真的是很難派上用場,而且還要維護。可是如果真的戰爭,那麼 100A 之類也是杯水車薪。
戰時用電發電機也是一個選項,可是汽油的來源也是不穩定。[謙品](https://www.championpower.com.tw/%E5%89%AF%E6%9C%AC-cp-200d)是有出一台瓦斯的發電機[CP-100](https://www.championpower.com.tw/%E5%89%AF%E6%9C%AC-cp-200d),但是一台二萬多塊,發電量也是 1000W連續功率 900W而一顆1.2 度電的電池價格大約是 12000元不用受限於瓦斯汽油但是受限於日照因素。
而且戰爭不一定爆發,我也很擔心我是不是緊張過頭,真是煩惱。
# 有什麼想法?
# 相對應的行動是什麼?

65
13. 常用資料/Datas.md Normal file
View File

@@ -0,0 +1,65 @@
# Family
## Wife
- 帳戶
- 中國信託新竹分行
- SWIFTCODECTCBTWTP299
- 台幣帳戶299511057829
- 外幣帳戶0000554131046751
## Ray
- HUANG PIN-JUI
- IDO100875958
## Denny
- IDO100968172
## Me
- No. 14, Aly. 101, Ln. 648, Minghu Rd., East Dist., Hsinchu City 300108 , Taiwan (R.O.C.)
- Logitech
- Empolyee ID239344
- Dept CCN.527200000
- ZOOM Direct number03-6122437
- ZOOM Extension No.862437
- [Qualcomm ID](https://myaccount.qualcomm.com/login)
- Account: [jteng2@logitech.com](mailto:jteng2@logitech.com)
- Password: Vn571jim@
- Software tool
- Office Professional Plus 2019 Key: `X88NP-YKCMM-DKVWJ-QYPWF-7H8J6`
- VS2013 key: `YDJWM-3WVQ7-JXMKW-DTHV3-2KXPG`
- Crypto coin
- ERC20: `0x9Ce80345355Ad8C17991620E13d8423900CEDcd0`
- ERC20: `0x9Ce80345355Ad8C17991620E13d8423900CEDcd0`
- 國泰證券
- 銀行交割帳號:`(013)699510287987`
- 證券交易帳號:`8888-3839353`
### 定期匯款帳號
#### 保險
006 合作金庫 0005643765393546
NT$9000
Total NT$56465, Avg NT$4705(原本6136)
| 保單 | 被保人 | 繳款日期 | 金額 |
|:--------------------------------------------:| ------:|:-------- | ------ |
| 國華人壽長期看護終身保險(96) (KHJV) | 黃玄揮 | 10/23 | 18,869 |
| 全球人壽活力一生終身醫療健康保險 (PHA) | 黃玄揮 | 10/24 | 14847 |
| 國華人壽關懷一生防癌終身健康保險(100) (KHJW) | 黃品睿 | 07/31 | 7,473 |
| 國華人壽GO活力終身醫療健康保險(甲型)(100) (KHKI) | 黃品睿 | 07/31 | 15,276 |
#### 家費
822 中國信託 0000299511057829
NT$70000
#### 股票定期定額-元大
806 元大銀行 0020162750308297
NT$22000
#### 股票定期定額-國泰
013 國泰世華 0000699510287987
NT$12000
#### 信貸
822 中國信託 0000034540315726
NT$35*500

View File

@@ -0,0 +1 @@
[AnthonyCalandra/modern-cpp-features: A cheatsheet of modern C++ language and library features.](https://github.com/AnthonyCalandra/modern-cpp-features)

39
20.02. CPP/C++17.md Normal file
View File

@@ -0,0 +1,39 @@
- 變數宣告的方式變了
- Old: `int a = 3;`
- New: `int a {3};`
- `if`裡面可以宣告變數
```cpp
if (auto a {3}; a > b) {
// Do something
}
```
- `unique_ptr`: 無法複製的指標
- 傳統方法:
```cpp
unique_ptr<uint8_t[]> buffer = new uint8_t[256];
```
- 新方法:
```cpp
auto buffer = std::make_unique<uint8_t[]>(256);
```
- `share_ptr`: 可以複製,但要避免循環參考問題
- 透過refernce傳遞array參數
- 考慮一個帶有長度的陣列要傳到function裡面但是又希望在function面可以指定陣列長度
```cpp
double value[] { 1.0, 2.0, 3.0 }; // Error!
double value[] { 1.0, 2.0, 3.0, 4.0, 5.0 }; // Pass!
double average(const double (&array)[5]) {
...
}
```
- 用 `std::string_view` 代替 `const std::string&`。
## Multi-Thread
### 使用`std::async`
- [C++ 使用 Async 非同步函數開發平行化計算程式教學](https://blog.gtwang.org/programming/cpp-11-async-function-parallel-computing-tutorial/)
- [std::atomic](https://en.cppreference.com/w/cpp/atomic/atomic)

View File

@@ -0,0 +1 @@
[C++20, How Hard Could It Be](https://docs.google.com/presentation/d/1HwLNSyHxy203eptO9cbTmr7CH23sBGtTrfOmJf9n0ug/edit?fbclid=IwAR2wToW9uFJuLtUR9nftfv9N9axXwPK7HmuJWqgVmCeXd1XJF7ySQIkNsJM&resourcekey=0-GH5F3wdP7D4dmxvLdBaMvw#slide=id.g1c5cc391dd_2_295)

97
20.02. CPP/C++20.md Normal file
View File

@@ -0,0 +1,97 @@
## Modules
Modules provide a new way to organize and manage large codebases, helping to improve build times, reduce code duplication, and increase modularity.
```cpp
// Example: math.cppm
export module math;
export int square(int x) {
return x * x;
}
export int cube(int x) {
return x * x * x;
}
// Example: main.cpp
import math;
int main() {
int result = math::square(5);
// result = 25
int result2 = math::cube(5);
// result2 = 125
}
```
## Concepts
Concepts provide a way to specify **_constraints_** on templates, making it easier to write **_generic_** code that works with a wide range of types.
```cpp
template <typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> T;
};
template <Addable T>
T add(T a, T b) {
return a + b;
}
int main() {
int x = 1, y = 2;
auto result = add(x, y);
// result = 3
}
```
## Ranges
Ranges provide a new way to manipulate **_sequences_** of data in C++, making it easier to write clean, readable, and efficient code.
```cpp
#include <ranges>
#include <iostream>
int main() {
std::vector<int> v{1, 2, 3, 4, 5};
auto even = v | std::ranges::views::filter([](int x) { return x % 2 == 0; });
for (int x : even) {
std::cout << x << '\n';
}
}
```
## Contract Programming
Contract programming allows developers to specify **_preconditions_**, **_postconditions_**, and **_assertions_** in their code, making it easier to **catch** bugs early and reduce the number of runtime errors.
```cpp
#include <iostream>
[[nodiscard]] int foo() {
return 42;
}
int main() {
int x = foo();
std::cout << x << '\n';
}
```
## Improved Template Metaprogramming
C++20 includes several imrovements to the way templates can be used in C++, making it easier to write **generic** code that can be used with a wide range of types.
```cpp
#include <type_traits>
template <typename T>
struct is_integral : std::false_type {};
template <>
struct is_integral<int> : std::true_type {};
int main() {
static_assert(is_integral<int>::value, "");
static_assert(!is_integral<float>::value, "");
}
```

View File

@@ -0,0 +1,23 @@
> Class template類別樣板不是類別而是建立類別的方法。
定義類別樣板
```cpp
template <template parameter list>
class ClassName
{
// Template class definition
};
```
`typename`來指定會變動的變數型態,例:
```cpp
template <typename T1, typename T2>
class MyTemplateClass
{
public:
T1 length;
T2 weight;
};
```

274
20.02. CPP/GCC.md Normal file
View File

@@ -0,0 +1,274 @@
GCC的全稱是GNU Compiler Collection是GNU工具鏈中的一種。GCC不僅支持C/C++語言還支持Fortran/Ada/Java等語言的編譯。GCC和gcc是兩個概念GCC是工具鏈的集合裡面除了gcc/g++還包含了cclcclplus等組件。gcc/g++只是GCC工具鏈的一個子集。
## g++和gcc的區別
gcc可以判斷出目標程序所使用編程語言的類別會把xxx.c文件當作C語言編譯把xxx.cpp文件當作C++語言編譯。而g++只把xxx.c和xxx.cpp一律都當作C++語言來編譯。在編譯C++文件的時候g++會自動鏈接一些標準庫或基礎庫而gcc不會。當正在編譯的C++代碼文件依賴STL標準庫的時候為了使用STLgcc命令需要增加參數`lstdc++`。因此雖然gcc和g++都可以編譯C++語言程序但是使用g++會更方便一些。
## 常見文件副檔名
- 目標文件:
- `xxx.o`Linux, Mac
- `xxx.obj`windows
- 二進製文件:
- `xxx`沒有副檔名Linux, Mac, FreeBSD,
- `xxx.exe`windows
- `xxx.hex`:嵌入式系統
- 共享庫文件,也叫動態庫文件:
- `xxx.dll`windows
- `xxx.so`Linux
- `xxx.dylib`Mac
- 靜態庫文件
- `xxx.a`
## C/C++語言的編譯過程
1. 預處理
預處理命令聲明了編譯時需要的各種頭文件和宏,比如包含哪些頭文件、宏定義的擴展、在哪個代碼段做條件編譯等。涉及預處理的語法有:#define,#include,#ifdef...#endif
2. 編譯
首先檢查代碼的規範性和語法錯誤等,檢查完畢後把代碼翻譯成彙編語言,生成彙編語言文件
3. 彙編
基於彙編語言文件生成二進制格式的目標文件
4. 鏈接
將目標代碼與所依賴的庫文件進行關聯或者組裝,合成一個可執行文件
具體過程如圖:
![[Pasted image 20220926211701.png]]
### 拿g++舉例
1. 樣例代碼:
```cpp
#include <iostream>
int main() {
    std::cout << "Hello World!" << std::endl;
    return 0;
}
```
2. g++的編譯第一步是預處理:將`xx.cpp`源文件預處理成`xx.i`文件
```cpp
g++ -E demo.cpp -o demo.i
```
3. 第二步是編譯:將`xx.i`文件編譯為`xx.s`的組合語言文件。此時只進行編譯生成組合語言程式碼,而不對代碼以彙編的方式調試
```js
g++ -S demo.i -o demo.s
```
3. 第三步是彙編:將`xx.s`文件彙編成`xx.o`的二進制目標文件
```cpp
g++ -c demo.s -o demo.o
```
4. 第四步是鏈接:將`xx.o`二進製文件進行鏈接,最終生成可執行程序
```cpp
g++ demo.o -o demo.out
```
![[Pasted image 20220926212043.png]]
## 靜態鏈接和動態鏈接的區別
### 靜態庫
與目標程序合併,成為目標程序的一部分。
創建靜態庫的時候,需要使用`gcc/g++ -c`先將xxx.c源文件編譯為目標文件xxx.o然後使用`ar`指令將xxx.o打包成xxxx.a靜態庫。
目標程序與靜態庫鏈接時,目標程序代碼調用的任何外部函數的代碼都會從靜態庫中復製到最終的可執行文件中。
GCC在鏈接時優先使用動態庫只有當動態庫不存在時才開始使用靜態庫如果要強制使用靜態庫編譯時加上`-static`參數。
使用`-Wl``-Bstatic`告訴鏈接器優先使用靜態庫。
### 動態庫
不包含在目標程序中,但是與目標程序相關聯。
創建動態庫的時候,可以傳`-shared``-fPIC`參數,`-fPIC`參數用於編譯階段,用來生成位置無關的代碼。使用`gcc -shared -fPIC`可以直接用xxx.c源文件生成xxx.so動態庫。
目標程序與動態庫鏈接時,可執行文件僅包含它所需的一個小函數表,而不是來自庫文件的完整機器代碼。在可執行文件開始運行之前,動態庫的代碼被操作系統複製到內存中進行共享。
動態庫之所以叫共享庫,可能是由於動態庫的代碼副本可以在多個程序之間共享。正因為這種鏈接方式,共享庫每次被更新時,都不需要重新編譯正在使用共享庫的目標程序。
使用`-Wl``-Bdynamic`告訴鏈接器優先使用動態庫。
### 有關的環境變量
`LIBRARY_PATH`:使用於編譯期間,目標程序鏈接時搜索動態庫的路徑。
`LD_LIBRARY_PATH`:使用於目標程序生成後,目標程序運行時搜索動態庫的路徑
### 靜態庫鏈接時,搜索庫文件路徑的順序
1. `ld`會去找GCC命令中的參數`-L`
2. gcc的環境變量`LIBRARY_PATH`
3. `/lib``/usr/lib``/usr/local/lib`等寫在程序內的路徑
### 動態庫鏈接時,搜索庫文件路徑的順序
1. 編譯目標代碼時指定的動態庫搜索路徑
2. gcc的環境變量`LD_LIBRARY_PATH`
3. 配置文件`/etc/ld.so.conf`中指定的動態庫搜索路徑
4. 默認的動態庫搜索路徑`/lib`
5. 默認的動態庫搜索路徑`/usr/lib`
## 實用的工具
### ldd
列出依賴的動態庫
![[Pasted image 20220926212505.png]]
### nm
查看動態庫/靜態庫中的函數
![[Pasted image 20220926212522.png]]
## gcc/g++命令常見參數
命令格式
```bash
gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-pedantic]
           [-Idir...] [-Ldir...]
           [-Dmacro[=defn]...] [-Umacro]
           [-foption...] [-mmachine-option...]
           [-o outfile] [@file] infile...
```
在linux環境使用`man g++`可以查看g++命令常用參數
### 常見參數如下(注意大小寫):
```bash
-o
#输出到指定文件。如果不指定默认输出到a.out
-E
#仅进行预处理,不进行编译、汇编和链接
-S
#将代码转换为文件格式为xxx.s的汇编语言文件但不进行汇编
-c
#仅进行编译和汇编不进行链接操作常用于编译不包含main程序的子程序代码
-v
#打印gcc编译时的详细步骤信息
复制代码
```
### 編譯和路徑參數
```bash
-l[basic library]
#编译时指定要使用的基础库,样例:-lpthread针对Posix线程共享库进行编译
-L[shared-library path]
#共享库的路径添加到搜索的范围路径为包含xxx.dll/xxx.so/xxx.dlyb文件的目录
-I[include header-file path]
#将头文件的路径添加到搜索的范围路径为包含xxx.h/xxx.hpp文件的目录
-shared
#生成共享库库文件格式为xxx.dll/xxx.so/xxx.dlyb格式的文件
-static
#生成静态库库文件格式为xxx.a格式的文件
-Wl
#告诉编译器将后面的参数传递给链接器
-Wl,-Bstatic
#-Bstatic选项用于对指定的库静态连接
-Wl,-Bdynamic
#-Bdynamic搜索共享库默认
-Wa,option
#此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然后传递给会汇编程序
-Wl,option
#此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然后传递给会连接程序
复制代码
```
### 預處理參數
```bash
#使用形式:-D[FLAG] 或-D[FLAG]=VALUE
-Dmacro
#在命令行里定义宏相当于C语言中的"#define macro"
-Umacro
#相当于C语言中的"#undef macro"
-undef
#取消对任何非标准宏的定义
复制代码
```
### 警告與報錯參數
```bash
-Wall
#发出gcc提供的所有有用的报警信息
-Werror
#将警告升级为编译报错
-Wextra / -W
#启用-Wall未启用的额外警告位对合法但值得怀疑的代码发出警告 例如 -Wsign-compare
-pendantic / -Wpendantic
#发出ISO C和ISO C++标准列出的所有警告,用于语法检查,-pedantic-erros的用法也类似
-fsyntax-only
#仅做语法检查
复制代码
```
### 調試參數
```bash
-g
#产生带有调试信息的目标代码
-gstabs
#此选项以stabs格式声称调试信息,但是不包括gdb调试信息
-gstabs+
#此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息
-ggdb
#生成gdb专用的调试信息
-glevel
#请求生成调试信息同时用level指出需要多少信息默认的level值是2
复制代码
```
### 編碼配置參數
```bash
-fno-exceptions
#屏蔽掉C++的异常,常用于于嵌入式或无法接受异常的系统
-fno-rtti
#禁用RTTI常用于嵌入式或游戏开发
-fno-asm
#不要识别asm,inline或typeof作为关键字以便代码可以使用这些词作为标识符。您可以使用关键字__asm__,__inline__来__typeof__ 代替。 -ansi暗示-fno-asm
-fPIC / -fpic
#让编译器的代码和位置无关,让代码逻辑不使用绝对地址,只用相对地址,方便文件加载
-nostdinc
#使编译器不再系统默认的头文件目录里面找头文件, 一般和 -I 联合使用,明确限定头文件的位置
-nostdin C++
#规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创建libg++库使用
复制代码
```
### 優化參數
```bash
-O0
#不优化
-O1 / -O
#尝试优化编译时间和可执行文件大小
-O2
#尝试所有的优化选项,但不会进行“空间换时间”的优化方式
-Os
#尝试所有的优化选项时,优先优化可执行文件大小
复制代码
```
## 來源
- [C/C++生態工具鏈——gcc/g++編譯器使用指南- 掘金](https://juejin.cn/post/7143280156042330125)
## 參考
- [Top (Using the GNU Compiler Collection (GCC))](https://gcc.gnu.org/onlinedocs/gcc/)
- [The C++ Compilation Model | C++ Fundamentals](https://subscription.packtpub.com/book/programming/9781789801491/1/ch01lvl1sec03/the-c-compilation-model)
- [What is LD_LIBRARY_PATH used for?](https://linuxhint.com/what-is-ld-library-path/)

View File

@@ -0,0 +1,16 @@
1. Use std::shared_ptr & std::unique_ptr & std::weak_ptr
2. Use std::array or std::vector
3. Use structured binding & std::tuple
4. Use for (auto& elem : collector)
5. Use std::format
6. Use std::optional
7. Use auto for return type
8. Use auto in variable declaration
9. Use `using` to replace `#define`
10. Use "Lambda expression" and std::function
11. `[[deprecated]]` attribute
12. Maybe...std::any?
13. And more, constexpr, concept,
## Reference
- [AnthonyCalandra/modern-cpp-features: A cheatsheet of modern C++ language and library features.](https://github.com/AnthonyCalandra/modern-cpp-features)

View File

@@ -0,0 +1 @@
[Modern C++ use in Chromium](https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/c++-features.md#Declaring-non_type-template-parameters-with-auto-tbd)

View File

@@ -0,0 +1,68 @@
---
tags: cpp17
aliases:
date: 2025-02-10
time: 17:34:18
description:
---
Structured binding declaration 可以把對應的 tuple、pair、vector 展開,讓 code 更好讀。
## 展開 tuple
假設我們有一個 tuple
```cpp
std::tuple<std::string, uint32_t, uint32_t> person{ "John", 32, 170 };
auto& [name, age, tall] = person;
```
name 會是 "John"
age 是 32
tall 是 170
但比較好用的時候還是用來展開 function 的回傳值,假設我們有一個 function 會回傳 tuple
```cpp
std::tuple<std::string, uint32_t, uint32_t> getPersonData(uint32_t id) {
return std::make_tuple("John", 32, 170);
}
auto [name, age, tall] = getPersonData(id);
```
## 展開 array
`std::vector` 也是一樣的用法:
```cpp
#include <array>
std::array<int, 3> my_vec = { 5, 7, 10 };
auto& [num1, num2, num3] = my_vec;
```
或是:
```cpp
float rect[4]{ 5.0f, 6.0f, 120.0f, 200.0f };
auto& [x, y, w, h] = rect;
```
但是不能用來展開 `std::vector`
## 展開 pair
```cpp
std::pair<std::string, int32_t> name_phone{ "John", 912345678 };
auto& [name, phone_number] = name_phone;
```
用在 for-loop 裡也比較好懂,假設我們有一個 vector 用來存剛剛的姓名跟電話:
```cpp
std::vector<std::pair<std::string, uint32_t>> phoneBook = {
{ "John", 912345678 },
{ "Andy", 912345679 },
};
for (const auto& [name, phone] : phoneBook) {
std::cout << "Name: " << name << ", phone: " << phone << std::endl;
}
```
# 參考來源

85
20.02. CPP/chrono.md Normal file
View File

@@ -0,0 +1,85 @@
---
tags: cpp11
aliases:
date: 2025-02-10
time: 17:38:00
description:
---
## header
```cpp
#include <chrono>
```
## duration
duration 是 chrono 裡面,用來記錄時間長度的類別,他基本上是一個 template class可以自行定義他的意義chrono 也有提供一些比較常見的時間類別可以直接拿來使用下面就是內建的 duration 的型別
```cpp
typedef duration<long long, nano> nanoseconds;
typedef duration<long long, micro> microseconds;
typedef duration<long long, milli> milliseconds;
typedef duration<long long> seconds;
typedef duration<int, ratio<60> > minutes;
typedef duration<int, ratio<3600> > hours;
```
其中可以看到,第一個 template 參數是要用來儲存資料的型別第二個則是他相對於「秒」的比例。這邊也使用了 ratio 這個 C++11 的另一個新的函式庫的類別,他是用來記錄「有理數」(可以寫成分數的數)的新類別,有興趣可以參考 [cppreference 的介紹](http://en.cppreference.com/w/cpp/numeric/ratio/ratio)。
基本上,一般會用到時間單位這邊都有定義好了,如果不合用的話,也可以自己去定義;而由於 chrono 也有把相關的計算都定義了,所以也可以直接拿來做計算,就算是時間單位不同,也不會有問題。
下面就是一個簡單的例子:
```cpp
std::chrono::minutes t1( 10 );
std::chrono::seconds t2( 60 );
std::chrono::seconds t3 = t1 - t2;
std::cout << t3.count() << " second" << std::endl;
```
其中t1 是代表 10 分鐘、 t2 是代表 60 秒t3 則是 t1 減去 t2也就是 600 60 = 540 秒。
而如果要取得一個 duration 的值的話則是要呼叫他的 count() 這個函式像在上面的例子裡面就會把 t3 的值輸出所以最後會出現「540 second」。
而如果想要做強制的時間單位轉換也可以使用 duration_cast<>() 這個函式來做下面就是一個把以秒為單位的 t3 轉換成分鐘後再輸出。
```cpp
cout << chrono::duration_cast<chrono::minutes>( t3 ).count() << endl;
```
## time_point
相較於 duration 是用來紀錄時間的長度的time_point 是用來記錄一個特定時間點的資料類別。他一樣是一個 template class需要指定要使用的 clock 與時間單位duration
Chrono 一般來說有提供兩種 clock 可以使用分別是system_clock  steady_clock。其中 system_clock 是直接去抓系統的時間有可能在使用中會被被修改[參考](http://en.cppreference.com/w/cpp/chrono/system_clock) steady_clock 則是確實地去紀錄時間的流逝所以不會出現時間倒退的狀況[參考](http://en.cppreference.com/w/cpp/chrono/steady_clock))。
一般要使用的話,大概會是下面的樣子:
```cpp
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
std::cout << "Hello World\n";
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
std::cout << "Printing took "
<< std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
<< "us.\n";
```
透過 clock 類別所提供的 now() 這個函式可以快速地取得現在的時間而兩者相減的話則會產生一個型別為 duration 的結果在上面的例子裡面就是一開始先取得當下的時間 t1然後輸出一個字串後、再去取得一個時間 t2之後兩者相減就可以取得中間過程所花費的時間了。在這邊則是在相減後把結果轉換成以 micro second 為單位後,再做輸出。
 time_point 也可以和 duration 做計算得出新的 time_point例如下面的程式碼就是計算 10 個小時候的時間:
```cpp
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::chrono::system_clock::time_point nt = now + std::chrono::hours(10);
```
另外chrono 也有定義 high_resolution_clock提供更高的精確度[參考](http://en.cppreference.com/w/cpp/chrono/high_resolution_clock));但是實際上在 MSVC11 上他就等同於 system_clock。
而如果要把不同定義的 time_point 做轉換則也可以使用 time_point_cast<>() 這個函式來處理,不過這邊就不多加說明了。
## time_point 的輸出
STL 的 chrono 並沒有定義 time_point 的輸出方式所以我們並不能直接透過 output stream 來輸出 time_point 的資料所以如果要把他輸出成字串的話其實還有點麻煩…
如果想要輸出的話,一個方法是透過 clock 提供的 to_time_t() 這個函式 time_point 先把他轉換成 C-style 的 time_t然後再透過 ctime() 這類的函式做輸出;下面是一個簡單的範例:
```cpp
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t( now );
std::cout << std::ctime( &now_c ) << std::endl;
```
而如果是使用 Boost 的版本的話Boost 則是另外有提供 chrono_io.hpp 這個檔案在裡面替 duration  time_point 定義了輸出的格式可以直接使用相當地方便有興趣的話可以參考 [Boost 的官方說明](http://www.boost.org/doc/libs/1_55_0/doc/html/chrono/users_guide.html#chrono.users_guide.tutorial.i_o)。
# 參考來源
- [C++11 STL 的時間函式庫chrono Heresy's Space](https://kheresy.wordpress.com/2013/12/27/c-stl-chrono/)

35
20.02. CPP/for_each.md Normal file
View File

@@ -0,0 +1,35 @@
---
tags: cpp17
aliases:
date: 2025-02-10
time: 17:37:31
description:
---
for_each 是一個 function它的原型是
```cpp
template<class InputIterator, class Function>
Function for_each(
InputIterator _Start,
InputIterator _Last,
Function _Func
);
```
它需要3個參數第1個是開始的iterator第2是結束的 iterator第3個是要用來處理的 function。
一個簡單的例子有一個array需要把每一個數都加1
```cpp
vector<int> arr1 = { 4, 5, 8, 3, 1 };
for_each(
arr1.begin(), // _Start
arr1.end(), // _Last
[](int& val) { // _Func
val += 1;
}
);
```
# 參考來源

223
20.02. CPP/lambda.md Normal file
View File

@@ -0,0 +1,223 @@
---
tags: cpp11, cpp14
aliases:
date: 2022-06-12
time: 18:21:42
description:
---
一個簡單的 Lamdba 運算式:
```cpp
[] (int x, int y) -> bool {
return x < y;
}
```
- 以中括號開頭,中括號被稱為*lamdba 導入器lamdba introducer*
- 小括號裡面是*lamdba 參數列表lambda parameter list*
- 如果沒有參數,小括號可以省略,`[] () {...}` 可以簡寫成 `[] {...}`
- 箭號(`->`)後面是回傳的型別,如果沒寫就由 `return` 自動推斷
將 Lamdba 運算式指定給變數:
```cpp
auto comapre = [] (int x, int y) -> bool {
return x < y;
};
```
## Lamdba的擷取子句
以中括號開頭的 *lamdba 導入器* 可以將外部的變數傳給 Lamdba 運算式正式名稱是「擷取子句capture clause」。
`[=]` 表示它們會以值擷取captured by value。Scope內的變數可以在 lamdba 內使用,但是不可以改變。
`[&]` 表示它們會以參考擷取captured by reference。Scope內的變數可以在 lamdba 內使用,可以改變。
## 以值擷取captured by value
假設有一段程式如下:
```cpp
void testLambda() {
float notUsed = 1.0f;
std::vector<int32_t> numlist{10, 20, 30, 50, 60};
auto findInRange = [=](int32_t start, int32_t end) {
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
};
std::cout << "Result: " << findInRange(25, 35) << "\n";
}
```
`[=]`可以用來擷取 lamdba scope 範圍所及的變數,沒有在 Lamdba 運算式裡面被用到的變數就不會被擷取,例如 `float notUsed = 1.0f;`
另一個重點是:**被擷取的變數是不可以更改的**。例如,不能在 lambda 裡面這樣寫:
```cpp
auto findInRange = [=](int32_t start, int32_t end) {
numlist.push_back(5); // ERROR!
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
};
```
如果一定要在 lambda 內改變擷取的變數,那必須指名 lambda 為 `mutable`
```cpp
auto findInRange = [=](int32_t start, int32_t end) mutable { // <-- assign mutable
numlist.push_back(5);
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
};
```
根據書上解釋 ,可以裡解為 compiler 會將 lamdba 編為一個 class像是
```cpp
class __Lambda8C1A5 {
public:
__Lambda8C1A5(const std::vector<int32_t>& arg1) : numlist(arg1) {}
auto operator()(int32_t start, int32_t end) const { // const!
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
}
private:
std::vector<int32_t> numlist;
};
```
這也解釋了 lamdba 的擷取範圍與原理。而 `mutable` 則是讓 `operator()` 不為 `const`,如下:
```cpp
auto findInRange = [=](int32_t start, int32_t end) mutable { // <-- assign mutable
numlist.push_back(5);
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
};
...
class __Lambda8C1A5 {
public:
__Lambda8C1A5(const std::vector<int32_t>& arg1) : numlist(arg1) {}
auto operator()(int32_t start, int32_t end) { // No const here
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
}
private:
std::vector<int32_t> numlist;
};
```
## 以值擷取特定的變數
若只需要擷取特定的變數,那就直接在 lamdba 導入器(就是`[]`)寫入變數名稱,例如:
```cpp
int var1 = 10;
int var2 = 20;
int var3 = 30;
auto afunc = [var1, var2] () {
...
};
```
## 以參考擷取captured by reference
`[&]` 會擷取 scope 內的所有外部變數,而且可以修改:
```cpp
void testLambda() {
float notUsed = 1.0f;
std::vector<int32_t> numlist{ 10, 20, 30, 50, 60 };
auto findInRange = [&](int32_t start, int32_t end) { // Use & here
numlist.push_back(100); // OK
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
};
std::cout << "Result: " << findInRange(25, 35) << "\n";
std::cout << "numlist: ";
for (auto n : numlist) {
std::cout << n << " ";
}
std::cout << "\n"; // Output numlist: 10 20 30 50 60 100
}
```
## 以參考擷取特定的變數
但是直接參考全部的外部變數不是好的作法,這讓你有機會做出一些意外的修改,所以請擷取有需要的變數就好:
```cpp
void testLambda() {
float notUsed = 1.0f;
std::vector<int32_t> numlist{ 10, 20, 30, 50, 60 };
auto findInRange = [&numlist](int32_t start, int32_t end) {
numlist.push_back(100); // OK
for (auto num : numlist) {
if (num >= start && num <= end) return true;
}
return false;
};
...
}
```
如果有多個變數需要擷取,那就用 `,` 分開:
```cpp
auto findInRange = [&numlist, &var1, &var2](int32_t start, int32_t end) {
...
};
```
## 混合擷取
以值擷取跟參考擷取也可以寫在一起:
```cpp
auto findInRange = [=, &numlist](int32_t start, int32_t end) {
...
};
```
上面的例子中,`numlist` 會是參考擷取,其他的外部變數則是以值擷取。
或是:
```cpp
auto findInRange = [&, numlist](int32_t start, int32_t end) {
...
};
```
上面的例子中,`numlist` 會以值擷取,其他的外部變數則是參考擷取。
但是,如果已經使用了 `=` ,就不可以再以值擷取其他變數,像是 `[=, numlist]` 就是不合法的。
反之,如果已經使用了 `&`,就不可以再參考擷取其他變數,像是 `[&, &var1]` 就是不合法的。
## 存取 class
Lamdba 寫在 class 裡面的時候,不論 [[lambda#以值擷取captured by value|以值擷取]]或是 [[lambda#以參考擷取captured by reference|以參考擷取]]都沒辦法傳遞成員變數member variable只能傳遞 `this`,透過 `this` 來存取成員變數。例:
```cpp
class BigBuffer {
public:
void modify(int x, int y, ...) {
auto modifyBuffer = [this] () { // Use this
if (buffer) { // equal to this->buffer
// do something with buffer
}
};
...
}
private:
uint32_t bufferSize = 0;
std::unique_ptr<uint8_t[]> buffer = nullptr;
};
```

3
20.02. CPP/lvalue.md Normal file
View File

@@ -0,0 +1,3 @@
lvalue 是指:
- 等號左邊的值
- 可以被「取址」的變數

View File

@@ -0,0 +1,70 @@
---
tags: cpp14
aliases:
date: 2025-02-10
time: 17:35:40
description:
---
move operator可以讓[[rvalue]]被參考從而進一部的消除複製的成本。例如以下的function會回傳一個很大的陣列
```cpp
vector<int> generateBigArray() {
const int size = 1000000;
vector<int> array;
for (int i = 0; i < size; i++) {
array[i] = i;
}
return array;
}
```
當我們呼叫這個function並把結果回傳到一個變數的時候每一次這個大陣列都會被複製一次
```cpp
vector<int> atemp = generateBigArray(); // 複製1000000個int
```
如果使用[[rvalue]] reference就可以避開這些複製
```cpp
vector<int>&& atemp = generateBigArray(); // 已經建立好的array會直接「移動」到atemp省下了複製的步驟
```
## move contructor
move contructor跟copy constructor很類似只是參數由`&`改成了`&&`
例:
```cpp
template <typename T>
inline Array<T>::Array(const Array&& moved) :
size{moved.size},
elements{moved.elements}
{
moved.elements = nullptr;
}
```
## move assignment operator
```cpp
template <typename T>
Array<T>& Array<T>::operator=(const Array&& rhs)
{
if (this != &rhs) {
delete [] elements;
elements = rhs.elements;
size = rhs.size;
rhs.elements = nullptr;
}
return *this;
}
```
## 明確的「移動」
如果有一個現存的「大東西」,可以使用`std::move`來把它「移」到別的地方,進而避開了複製的行為。例:
```cpp
std::vector<std::string> my_dictionary(10000000);
std::vector<std::string> dictionary2 = std::move(my_dictionary);
```
`std::move`之後my_dictionary的size會變成0。
# 參考來源

210
20.02. CPP/rvalue.md Normal file
View File

@@ -0,0 +1,210 @@
rvalue 是指:
- 等號右邊的值
- 臨時的值,例如運算的結果
- 無法被取址address-of的物件
## rvalue reference
一般的參考只能參考[[lvalue]]如下的程式是ok的
```cpp
int a = 10;
int& b = a;
```
但是像這樣就不行了:
```cpp
int a = 10;
int b = 5;
int& c = a + b;
```
因為`a+b`是一個 rvalue臨時的值沒辦法取址所以無法參考。
但是可以用`&&`來參考 rvalue。例如
```cpp
int a = 10;
int b = 5;
int&& c = a + b; // c = 15
```
而不用這樣:
```cpp
int a = 10;
int b = 5;
int r = a + b;
int& c = r;
```
了解 rvalue reference 之後,就可以實作類別的 move constructor 跟 move assignment operator。這可以減少複製的成本。
## Move constructor
假設我們有一個 class 叫 BigBuffer定義如下
```cpp
class BigBuffer {
public:
BigBuffer(int size=100*1024*1024) :
bufferSize(size)
{
std::cout << "BigBuffer constructor\n";
this->buffer = std::make_unique<uint8_t[]>(bufferSize);
}
~BigBuffer() {
std::cout << "BigBuffer destructor\n";
}
BigBuffer(const BigBuffer& src) {
std::cout << "BigBuffer copy constructor\n";
bufferSize = src.bufferSize;
buffer = std::make_unique<uint8_t[]>(bufferSize);
std::memcpy(buffer.get(), src.buffer.get(), bufferSize);
}
BigBuffer& operator= (BigBuffer& src) {
std::cout << "BigBuffer copy operator\n";
bufferSize = src.bufferSize;
buffer = std::make_unique<uint8_t[]>(bufferSize);
std::memcpy(buffer.get(), src.buffer.get(), bufferSize);
return *this;
}
private:
int bufferSize = 0;
std::unique_ptr<uint8_t[]> buffer = nullptr;
};
```
這個 class 的特色就是每一次使用都會佔用100MB的記憶體空間想像下面的程式的動作
```cpp
BigBuffer buf1;
// Do something with buf1
// Assign to buf2
BigBuffer buf2 = buf1;
```
執行訊息:
```
BigBuffer constructor // create buf1
BigBuffer copy constructor, copy 104857600Bytes // copy buf1 to buf2
...
```
這會先產生 buf1然後把 buf1 copy 給 buf2。如果我們想要省下 copy 的成本,這時候 Move constructor 就可以派上用場了。
幫 BigBuffer 加一個 Move constructor
```cpp
class BigBuffer {
public:
...
BigBuffer(BigBuffer&& src) noexcept {
std::cout << "BigBuffer move constructor\n";
bufferSize = src.bufferSize;
buffer = std::move(src.buffer);
src.buffer.reset();
src.bufferSize = 0;
}
...
};
```
這個 move constructor 的參數就是一個 rvalue reference我們把來源的 bufferSize 跟 buffer 指標「移到」我們這邊,而不是完整的複製一份。在轉移之後呢,當然也要把來源清空,讓轉移更加明確。
有了 Move assignment operator 之後,在執行一次原本的程式,你會發現訊息......沒有變,還是一樣呼叫 copy constructor 來複製了100MB 的 buffer這時我們需要明確的告訴 compiler 我們要「移動」物件,而不是複製它,把原本的程式改為:
```cpp
BigBuffer buf1;
// Do something with buf1
// Assign to buf2
BigBuffer buf2 = std::move(buf1);
```
我們用 `std::move()` 來「移動」物件,這時輸出變成
```
BigBuffer constructor // create buf1
BigBuffer move constructor // move buf1 to buf2, buf1 has nullptr now
...
```
另外一個情形也可以受益於此,假如我們有個 function 會產生 `BigBuffer`,如下:
```cpp
BigBuffer BigBufferCreator() {
std::cout << "BigBufferCreator: Create a BigBuffer!\n";
BigBuffer tempb;
// do something
std::cout << "BigBufferCreator: return\n";
return tempb;
}
BigBuffer b = BigBufferCreator(); // copy tempb to b
```
在沒有 Move constructor 的情況下,上面的程式會先產生一個 `tempb`,然後複製給 `b`,訊息:
```
BigBufferCreator: Create a BigBuffer!
BigBuffer constructor
BigBufferCreator: return
BigBuffer copy constructor, copy 104857600Bytes // Copy 100MB!
...
```
在有 Move constructor 的情況下,訊息就變成:
```
BigBufferCreator: Create a BigBuffer!
BigBuffer constructor
BigBufferCreator: return
BigBuffer move constructor // Use MOVE!
BigBuffer destructor
BigBuffer destructor
```
因為 `BigBufferCreator()` 產生的就是一個 `BigBuffer` rvalue所以 compiler 會使用 move constructor`BigBuffer(BigBuffer&& src)` 而不是 copy constructor。
## Move assignment operator(`=`)
Move assignment operator 的行為跟 move constructor 是一樣的,幫 `BigBuffer` 加入 move assignment operator
```cpp
class BigBuffer {
public:
...
BigBuffer& operator=(BigBuffer&& src) noexcept {
std::cout << "BigBuffer move operator\n";
bufferSize = src.bufferSize;
buffer = std::move(src.buffer);
src.buffer.reset();
src.bufferSize = 0;
return *this;
}
...
};
```
測試程式:
```cpp
BigBuffer b1, b2;
b2 = b1;
```
訊息:
```
BigBuffer constructor
BigBuffer constructor
BigBuffer copy operator, copy 104857600Bytes
```
還是使用 copy assignment operator 來複製,理由是一樣的,需要一個明確的 `std::move()` 來表示「轉移」的行動,把程式改成:
```cpp
BigBuffer b1, b2;
b2 = std::move(b1);
```
這樣就可以了。訊息:
```
BigBuffer constructor
BigBuffer constructor
BigBuffer move operator // Use MOVE!
```
## 參考
- [Value categories - cppreference.com](https://en.cppreference.com/w/cpp/language/value_category)
- [rvalue 參考](https://openhome.cc/Gossip/CppGossip/RvalueReference.html)
- [Move constructors - cppreference.com](https://en.cppreference.com/w/cpp/language/move_constructor)
- [Move assignment operator - cppreference.com](https://en.cppreference.com/w/cpp/language/move_assignment)

File diff suppressed because it is too large Load Diff

110
20.02. CPP/智慧指標.md Normal file
View File

@@ -0,0 +1,110 @@
[`unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)與[`shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr)都是智慧指標箱對於原本的raw pointer智慧指標使用起來更方便也不用擔心delete的問題。
## unique_ptr
`unique_ptr` 的特點是,它保證在一個時間內,只會有一個指標的擁有者,也就是這個指標不能被複製跟移動,當 `unique_ptr` 離開它的scope時候它所擁有的指標也隨之被delete。這讓你不用擔心memory leak的問題。
假設我們有一個class叫 `BigBuffer` ,原本分配記憶體的方法:
```cpp
BigBuffer* bigBuf = new BigBuffer(bufferSize);
// Use buffer here
delete bigBuf;
```
`unique_ptr`
```cpp
auto bigBuf = std::make_unique<BigBuffer>(bufferSize);
// Use buffer here
// bigBuf will be released when exiting scope
```
我們統一用[`std::make_unique<>`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)這個template function來建立 `unique_ptr` ,角括號 `<>` 裡面要帶入你要建立的型別,後面的括號 `()` 就是型別的constructor使用起來跟 `new` 是一樣的。
因為 `std::make_unique<>` 裡面已經有表明型別了,所以變數就用 `auto` 就可以了,不用再寫一次型別。
一旦 `unique_ptr` 建立之後,使用起來就跟一般指標沒有兩樣,都是用 `->` 來操作:`bigBuf->setXXX()` or `bigBuf->getXXX()`
但是別忘記 `unique_ptr` 本身還是一個local variable所以我們可以用 `.` 來操作 `unique_ptr` ,例如我們可以用 `.reset()` 重新配一個指標:
```cpp
BigBuffer* pBuffer = new BigBuffer();
bigBuf.reset(pBuffer);
```
這時候舊指標會自動delete如果記憶體分配有成功的話bigBuf會接管剛剛new出來的指標或者變成 `nullptr` (記憶體分配失敗)。
如果單純想要釋放指標,那就單純的呼叫 `reset()` 就好。
```cpp
bigBuf.reset(); // Now I'm nullptr
```
如果要分配陣列的話:
```cpp
auto intArray = std::make_unique<int[]>(1024);
```
使用方式也是一樣的:
```cpp
intArray[5] = 555;
```
不過對於陣列的操作更建議使用 `std::array`
如果有什麼特殊原因讓你決定不再讓 `unique_ptr` 來幫你管理指標,可以用 `release()` 來讓出指標:
```cpp
auto intArray = std::make_unique<int[]>(1024);
int* intArrayRaw = intArray.release(); // Now I don't care anymore
```
但是這時候呼叫 `delete[]` (或 `delete` )的責任又回到你身上了。所以千萬不要把 `release()``reset()` 搞混了。
`unique_ptr` 不能被複製跟移動,所以下列的寫法都編不過:
```cpp
auto ptr1 = std::make_unique<int>(5);
std::unique_ptr<int> ptr2(ptr1); // Error
std::unique_ptr<int> ptr2 = ptr1; // Error
```
在Visual Studio 2017上錯誤訊息是這樣`error C2280: 'std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)': attempting to reference a deleted function`
其實就是`unique_ptr`的copy constructor跟assignment operator都被標記為delete了。
### Move a unique_ptr
如果一定要把 unique_ptr 指定給別人可以嗎?可以的,用 `std::move()` 來轉移:
```cpp
auto ptr1 = std::make_unique<int>(5);
// do something
auto anotherPtr = std::move(ptr1);
```
ptr1原本所管理的指標會轉移給 anotherPtrptr1 會變成 nullptr。
## shared_ptr
建立一個 `shared_ptr` 是使用[`std::make_shared()`](https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared)
```cpp
auto myBuf = std::make_shared<BigBuffer>(bufferSize);
```
但是 `shared_ptr` 可以被複製與移動,這是跟 `unique_ptr` 的差別:
```cpp
auto myBuf = std::make_shared<BigBuffer>(bufferSize);
std::shared_ptr<BigBuffer> bufCopy = myBuf;
```
現在 bufCopy 跟 myBuf 都指向同一個指標,他們都可以操作這個指標:
```cpp
myBuf->setZero(startAddr, endAddr);
bufCopy->setOne(startAddr, endAddr);
```
`shared_ptr` 內部有一個參考記數reference count來紀錄它所擁有的指標已經分享給幾個變數了只要有變數離開了他的scope參考記數就會減少反之要是像上面那樣有人複製指標參考記數就會增加參考記數歸0的時候指標就會被釋放。
有了 `shared_ptr` 我們就不必擔心 delete 的責任問題:
```cpp
std::shared_ptr<BigBuffer> getBuffer(int32_t bufferSize) {
return std::make_shared<BigBuffer>(bufferSize);
}
int main() {
auto myBuf = getBuffer(1024); // new(malloc) memory
// use myBuf
return 0;
} // myBuf delete memory here
```
`shared_ptr` 有一個問題是可以會「循環參考」cyclic references也就是 share_ptr A 指向另一個 share_ptr B ,然後 share_ptr B 又指向 share_ptr A這造成參考記數reference count不會減少而永遠無法釋出指標。這個是需要注意的。
但是 `shared_ptr` 還是讓記憶體的管理問題大大減少,應該用 `shared_ptr` 來代替 `new` & `delete`

View File

@@ -0,0 +1,912 @@
# 剛開始
## 先說說Rx
Rx最早是Microsoft的某個實驗室為了解決asychronous、scalable還有一些app問題而提出來的libraray。大概在2009年的時候提出叫做Reactive Extension for .NET(Rx). 一開始是以add-on的方式安裝在.NET 3.5上,到了.NET 4.0就變成了內建的library。也因為它open source的關係讓其他語言得以將這套概念也移植過去所以現在有RxJS, RxSwift, RxNET, RxScale, RxJava。這些library都致力於在它們的語言來實作出相同的「行為」所以理論上iOS工程師可以和Web工程師用Rx來討論app的邏輯是沒有問題的。
Rx的官網[http://reactivex.io/](http://reactivex.io/)它的logo是一支電鰻Electric eel
![[Rx_Logo_S.png]]
Rx Community
- http://android-united.community/
- https://kotlinlang.slack.com/
什麼是RxJava
> RxJava is a library for composing asynchronous and event-based code using observable sequences and functional style operations, allowing for parameterized execution via schedulers.
> RxJava, in its essence, simplifies developing asynchronous programs by allowing your code to react to new data and process it in a sequential, isolated manner. In other words, RxJava lets you observe sequence of asychronous events in an app and respond to each event accordingly. Examples are taps by a user on the screen and listening for results if asynchronous network calls.
## 再說RxJava
RxJava是一個實作Rx的framework。
RxJava與其他的Rx library提供了asynchronous與event-based的解決辦法
而Asychronous code跟Sychronous code的差異
Sychronous code按照字面上的意思執行每一次的結果都相同。
Asychronous code則是在必要的時候才被使用每一次執行的「狀態」不盡相同。也就是沒辦法控制其順序與時間。
### Asychronous programming的詞彙
#### 1. State
- State指的是我們程式所儲存的資料與程式自身行為互動所產生的狀態。
-
#### 2. Imperative programming
- Imperative programming指令式程式設計是用一連串的命令或描述來改變程式的狀態。如下面的code
```
setupUI()
bindClickListeners()
createAdapter()
listenForChanges()
```
這些code可能有一些相關的邏輯但是字面上看不出來即使互相調換可能會造成錯誤但也可能不會。
#### 3. Side effect
- Side effect指的是「一段程式修改了它本身區域外的狀態」譬如說一個處理event的function它除了處理event之外也改變的UI上所顯示的文字。
- Side effect並不總是不好的我們的程式就是要對某些東西做出改變完全無法改變任何東西的程式是沒有用的。
RxJava試著用接下來的2個概念來解決剛剛提到的3個概念上的問題
#### 4. Declarative code
- 又叫Fucntional programmingFucntional programming不產生任何side effect。
- Declarative code定義的是行為。
- RxJava試著在Declarative code和Imperative programming取一個平衡點它定義行為然後依順序執行。
#### 5. Reactive systems
Reactive systems通常有幾個特性
- Reponseive保持UI在最新狀態
- Resilient每個行為都是獨立定義的而且有辦法靈活的處理錯誤。
- Elastic程式的十座可以處理不同的工作量
- Message driven每個元件使用Message driven訊息驅動的方式來互相溝通並改進可用性與獨立性解開decouple生命週期與實作的關聯。
### Rx的三大組成
#### 1. Observables
`Observable<T>`是Rx的基礎之一Observable允許觀察者觀察它並接收它發出來的資料。
##### Observables 的基礎event
Observable會以3種事件event來發出資料
1. **next****next** event會伴隨著一筆資料這也是觀察者用來接收資料的event。
2. **complete****complete** event表示Observable已經「成功的」結束了它的生命週期在**complete** event之後觀察者不會再收到任何**next** event。
3. **error****error** event表示Observable在發生錯誤的情況下結束它的生命週期。跟**complete** event依樣後續不會再有任何**next** event。
一個Observable用next所發出來的一連串資料我們稱為"sequence"。sequence可以分為兩種
1. Finite sequnece
想像你要下載一個檔案我們的code大概是這個樣子
```kotlin
API.download(file = "http://www...")
.subscribeBy(
onNext = {
// Handle downloading here
},
onComplete = {
// Handle download finish here
},
onError = {
// Handle error here
},
)
```
`API.download()`會產生一個Obervable然後我們藉由`subscribeBy`來訂閱他,並加入我們的處理程序,我們在`onNext`裡面處理接收到的檔案buffer在`onComplete`裡面了解到檔案已經完成下載,可以做一些後續的處理,`onError`則是發生了某些錯誤,需要重來或是通知使用者之類。
2. Infinite sequence
Switch button就是一個例子我們要處理switch button的code會是這樣
```kotlin
switch.checkdChanges()
.subscribeBy(
onNext = { isOn ->
if (isOn) {
// Handle on here
} else {
// Handle off here
}
}
)
```
可以看到這一段`subscribeBy()`裡面並沒有`onComplete`跟`onError`因為switch button根本就不會產生這兩種event。
#### 2. Operators
Operators用來處理Observable所發出來的資料可能是過濾或者做一些轉換或其他操作。再以switch button做例子下面的code可以把switch button的狀態做幾個改變
1. 我們只想收到on的狀態。
2. 把on的狀態轉為一個字串"We've been toggled on!"。
```kotlin
switch.checkdChanges()
.filter { it == true }
.map { "We've been toggled on!" }
.subscribeBy(
onNext = { message ->
updateTextView(message)
}
)
```
#### 3. Schedulers
Scheduler可以想像成threadRxJava已經內建了好幾種scheduler而且應該可以適用於大部分的情形。
例如IO scheduler可以讓你的檔案下載在背景執行`TeampolineScheduler`可以讓你的程式同時執行, `ComputationScheduler`可以讓你將程式分配給不同的thread來處理需要大量運算的資料。
RxJava是一個很獨立的library所以有2個library可以跟RxJava一起合作
1. RxAndroid提供Android Looper class跟RxJava的scheduler之間的橋接管道。
2. RxBinding用來把UI的click listen之類的callback轉變為Observable的`subscribeBy`。
# 安裝
在`build.gradle`裡的`depedencies`區域加入:
```
implementation "io.reactivex.rxjava3:rxjava:3.0.2"
implementation "io.reactivex.rxjava3:rxkotlin:3.0.0"
implementation "io.reactivex.rxjava3:rxandroid:3.0.0"
```
# Observable
Standard Observable has three types of event:
1. next
2. complete
3. error
Obervable很適合用marble diagram來表示
![[Pasted image 20210120150947.png]]
3個event解釋如下
1. `onNext()``onNext()` event會伴隨著一筆資料這也是觀察者用來接收資料的event。
2. `onComplete()``onComplete()` event表示Observable已經「成功的」結束了它的生命週期在`onComplete()` event之後觀察者不會再收到任何`onNext()` event。
3. `onError()``onError()` event表示Observable在發生錯誤的情況下結束它的生命週期。跟`onComplete()` event依樣後續不會再有任何`onNext()` event。
另外要注意一個Observable在沒有被訂閱的情況下「**是不會發送任何event的**」。
A example of usage of standard Observable:
```kotlin
API.download("http://...")
.subscribeBy(
onNext = { /* do something */ },
onComplete = { /* do something */ },
onError = { /* do something */ },
)
```
## 建立`Observable`的方法
### 1. `just`
```kotlin
val observable = Observable.just(1, 2, 3)
```
變數observable的內容設為1個"1、2、3"三個數,型別會是`Observable<Int!>!`。
如果使用了onNext來發送event的話將會依序發送1、2、3。
但如果是:
```kotlin
val observable = Observable.just(listOf(1, 2, 3))
```
變數observable的內容會是一個list這個list的內容是"1、2、3"。型別是`Observable<List<Int>!>!`。
如果使用了onNext來發送event的話將發送一個包含1、2、3的list。
### 2. `fromIterable`
用來將list的內容轉變為一個一個單獨的element給Observable。
```kotlin
val observable = Observable.fromIterable(listOf(2, 3, 1))
```
變數observable的型別會是`Observable<Int!>!`,而不是`Observable<List<Int>!>!`。
### 3. `empty`
建立一個「空的」Observable可以用來表示一個馬上就會結束的事情或是不包含任何東西的情況。
```kotlin
val observable = Observable.empty<Unit>()
observable.subscribeBy(
onNext = { println(it) },
onComplete = { println("Completed") }
)
```
用`empty()`所建立的observable只會發出`onComplete()` event所以上面的`onNext()` event永遠不會發生。
還有Observable所包含的element一定要有一個型別而且不可以是null所以上面的`empty()`必須明白的寫出`Unit`型別:`empty<Unit>() `。
### 4. `never`
建立一個不會發出任何event的observable。
### 5. `range`
產生一個範圍的數列,參數型別必須是整數(`Int`)。
```kotlin
val observable = Observable.range(1, 10)
```
上例的`onNext()`會依序發送1~10的數字出來。
### 6. `create`
用來定義自己的event發送方法。
範例:
```kotlin
val observable = Observable.create<String> { emitter ->
emitter.onNext("A")
emitter.onNext("C")
emitter.onNext("B")
emitter.onComplete()
}
val subscription = observable.subscribeBy(
onNext = { println("Received: $it") },
onComplete = { println("Completed") },
onError = { println("Completed") }
)
```
`create`必須帶入要發送的型別,例如`Int`、`String`或是任何class此例中是`create<String>`表示會送出的element是`String`型別。
然後`create`則是發送的實作,範例是會發送"A" -> "C" -> "B",然後用`onComplete`來結束。
注意要是observable沒有`onComplete`或是`onError`,然後`Disposable`(也就是訂閱者)也沒有呼叫`dispose()`則會造成memory leak。
### 7. `defer`
`defer`會建立一個Observable factory每一次呼叫這個factory都會產生一個新的Observable。`defer`只有一個參數就是我們要「製造」Observable的方法
```kotlin
var flip = false
val factory: Observable<Int> = Observable.defer {
flip = !flip
if (flip) {
Observable.just(1, 2, 3)
} else {
Observable.just(4, 5, 6)
}
}
```
`defer`後面的lambda就是我們要「製造」Observable的方法。當`flip`是`true`的時候,我們產生`Observable.just(1, 2, 3)`,反之則產生`Observable.just(4, 5, 6)`。Observable裡面所帶的element都是整數這也是為什麼factory的型別是`Observable<Int>`。
接下來訂閱這個factory
```kotlin
for (i in 0..3) {
val subscription = factory.subscribe {
println("Factory out: $it")
}
disposables.add(subscription)
}
disposables.dispose()
```
上面的例子產生了4個Observable。依照flip的值來產生不一樣內容的Observable。
How to subscrible a Observable
## 訂閱`Observable`的方法
1. `observable.subscrible()`
2. `observable.subscribleBy()`
Remember to release the resource. Call `disposable()` if you don't need Observable anymore. Or use `CompositeDisposable()` to collect all Disposable and release them.
## 特殊的Observable
### 1. `Single`
`Single`只有`onSuccess`跟`onError`兩種event。在發出`onSuccess`或是`onError`之後,`Single`就結束了。
譬如說讀取檔案,只會有讀取成功跟讀取失敗兩種情況,下面的範例讀取一個檔案,要是檔案不存在就發送`onError()`,反之就發送`onSuccess()`。
```kotlin
val subscriptions = CompositeDisposable()
fun loadText(filename: String): Single<String> {
return Single.create create@{ emitter ->
val file = File(filename)
if (!file.exists()) {
emitter.onError(FileNotFoundException("Can't find $filename"))
return@create
}
val contents = file.readText(Charsets.UTF_8)
emitter.onSuccess(contents)
}
}
// Use the single observable
val subscription = loadText("Copyright.txt")
.subscribeBy(
onSuccess = { println("Success read: $it") },
onError = { println("Error: $it") }
)
subscriptions.add(subscription)
```
`loadText()`這個function會返回`Single<String>`物件,要是讀取檔案成功,就把檔案內容用`onSuccess()`發送出來:
```kotlin
val contents = file.readText(Charsets.UTF_8)
emitter.onSuccess(contents)
```
要是檔案不存在,就發出`onError()`
```kotlin
emitter.onError(FileNotFoundException("Can't find $filename"))
```
### 2. `Completable`
`Completable`只有`onCompleted`跟`onError`兩種event。跟`Single`一樣,在發出`onCompleted`或是`onError`之後,`Completable`就結束了。
### 3. `Maybe`
`Maybe`是`Single`跟`Completable`的混合,他有`onSuccess(value)`、`onCompleted`跟`onError`三種event。`Maybe`只會發出這三種的其中一種event然後就結束了。
## 停止訂閱或是結束一個`Observable`
### 使用`Disposable.dispose()`
每一次呼叫`observable.subscrible()`或是`observable.subscribleBy()`都會回傳一個`Disposable`物件當我們不再需要訂閱一個Observable的時候我們必須呼叫`dispose()`停止訂閱:
```kotlin
val alphaSequnce = Observable.just("A", "B", "C")
val subscription = alphaSequece.subscribe {
println(it)
}
subscription.dispose()
```
### 使用`CompositeDisposable.dispose()`
對每一個`Disposable`物件在停止訂閱之後都要呼叫一次`dispose()`是很煩人的RxJava提供了一個`CompositeDisposable` class。它可以收納所有的`Disposable`物件,然後一次停止:
```kotlin
val subscriptions = CompositeDisposable()
val subscriptionNumbers = Observable.just(1, 2, 3).subscribe {
println("Numbers: $it")
}
val subscriptionAlphabets = Observable.just("A", "B", "C").subscribe {
println("Alphabets: $it")
}
subscriptions.add(subscriptionNumbers)
subscriptions.add(subscriptionAlphabets)
subscriptions.dispose() <-- subscriptionNumbers 與 subscriptionAlphabets 都會一起呼叫dispose()
```
忘記呼叫`dispose()`可能會造成memory leak。
# Subjects
Observable必須在建立的時候就指定好資料之後沒辦法再新增資料。而Subject可以在建立資料之後再新增資料Subject也會將新增的資料再馬上轉發給它的訂閱者。
## 1. `PublishSubject`
`PublishSubject`剛開始是沒有任何資料的,它也只會將最新的資料發送給它的訂閱者。另外,要是`PublishSubject`本身結束了(已經送出了`onComplete` event那麼新的訂閱者將不會收到任何資料但是會收到`onComplete` event。
```kotlin
val publishSubject = PublishSubject.create<Int>()
publishSubject.onNext(0)
val subscriptionOne = publishSubject.subscribe {
println(it)
}
publishSubject.onNext(1)
publishSubject.onNext(2)
val subscriptionTwo = publishSubject.subscribe {
println("2: $it")
}
publishSubject.onNext(3)
subscriptionOne.dispose()
publishSubject.onNext(4)
publishSubject.onComplete()
publishSubject.onNext(5)
subscriptionTwo.dispose()
val subscriptionThree = publishSubject.subscribeBy(
onNext = { println("3: $it") },
onComplete = { println("3: Completed") }
)
```
上例中的`subscriptionThree`只會收到`onComplete` event也就是只會印出`"3: Completed"`。
## 2. `BehaviorSubject`
行為跟`PublishSubject`類似,但是`BehaviorSubject`會發送「最後一筆資料」給新的訂閱者。如果`BehaviorSubject`最後的event是`onError`,那麼新的訂閱者也會收到`onError` event。例
```kotlin
val subscriptions = CompositeDisposable()
val behaviorSubject = BehaviorSubject.createDefault("Initial value")
behaviorSubject.onNext("X")
val subscriptionOne = behaviorSubject.subscribeBy(
onNext = { println("1: $it") },
onError = { println("1: ERROR, $it") }
)
behaviorSubject.onError(RuntimeException("Error!"))
behaviorSubject.onNext("Y") // <-- 不會有人收到因為已經被onError給terminate了
subscriptions.add(behaviorSubject.subscribeBy(
onNext = { println("2: $it") },
onError = { println("2: $it") }
))
```
另外,可以直接取得`BehaviorSubject`目前的值,以上例來說,只要用`behaviorSubject.value`就可以這方法可以很方便的在Rx與非Rx的程式中交換資料。
例子中是用static method `BehaviorSubject.createDefault()`來建立一個有初始值的`BehaviorSubject`,當然也可以跟`PublishSubject`一樣,用`BehaviorSubject.create()`來建立。
## 3. `ReplaySubject`:
`BehaviorSubject`會發送會後一筆資料,`ReplaySubject`就是發送最後n筆資料。我們可以用`ReplaySubject.createWithSize()`這個static method來建立一個`ReplaySubject`。例:
```kotlin
val replaySubject = ReplaySubject.createWithSize<String>(2)
```
變數`replaySubject`的buffer容量是2型別是`String`。
## 4. `AsyncSubject`
`AsyncSubject`的行為比較特別,`AsyncSubject`只會結束的時候,同時發出最後一筆資料。也就是說,即便一直提供資料給`AsyncSubject`,它也不會發出任何`onNext` event給它的訂閱者直到它收到`onComplete`的時候,它才會同時發出最後一筆`onNext`與`onComplete`給它的訂閱者。
## 5. `RxRelay`
`RxRelay`永遠不會發出`onComplete`或是`onError`。下面例子建立了一個`PublishRelay`
```
val publishRelay = PublishRelay.create<Int>()
```
要使用`RxRelay` library必須在build.gradle裡面加入
```
implementation "com.jakewharton.rxrelay3:rxrelay:3.0.0"
```
# Operators
## 1. Filtering Operators
### `ignoreElement`
`ignoreElement()`會忽略掉由[[20200207 - Study RxKotlin#Subjects]]丟出來的**next** event訂閱者只會收到`onCompleted`跟`onError`這兩種event也就是讓Subject退化成[[20200207 - Study RxKotlin#2 Completable]]。
例:
```kotlin
val subscriptions = CompositeDisposable()
val strikes = PublishSubject.create<String>()
subscriptions.add(
strikes.ignoreElements()
.subscribeBy {
println("Done") <-- 只會收到onComplete跟onError
}
)
```
### `elementAt`
`elementAt()`只會處理「第n個」**next** eventn之前跟n之後的都會被忽略。例如
```kotlin
val subscriptions = CompositeDisposable()
val strikes = PublishSubject.create<String>()
subscriptions.add(
strikes.elementAt(2) <-- 只要收第2個
.subscribeBy {
println("Get $it")
}
)
strikes.onNext("A") <-- 第0個
strikes.onNext("B") <-- 第1個
strikes.onNext("C") <-- 第2個
strikes.onNext("D") <-- 第3個
```
上面例子只要收「第2個next event」所以只會收到"**C**"。這也是一個`onSuccess` event。
`elementAt()`也等於是把Subject退化成[[20200207 - Study RxKotlin#3 Maybe]]。
要是Subject的「第n個」還沒收到就結束了那就是收到`onComplete` event。
### `filter`
`filter()`接收一個lambda函數每一個next event所帶的element都必須經過這個函數的「驗證」只有驗證結果為`true`的時候才會pass給訂閱者。例
```kotlin
val subscriptions = CompositeDisposable()
subscriptions.add(
Observable.fromIterable(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
.filter {
it > 5 <-- 數值必須大於5才可以pass給訂閱者
}
.subscribe {
print("Get number: $it\n")
}
)
```
所以上面的結果會收到6、7、8、9、10。
### `skip`
忽略「前n個」next event。
```kotlin
Observer.just(1, 2, 3, 4 ,5)
.skip(3) <-- 忽略前3個
.subscribe {
println("it")
}
// Output 4, 5
```
### `skipWhile`
`skipWhile`也是用一個lambda來當作通過條件跟[[20200207 - Study RxKotlin#filter]]類似。但不像`filter`會去檢查「每一個」進來的element`skipWhile`是「當lambda檢查到第一個`false`的時候,後面全部通過」。
另一個跟`filter`不同的是,`skipWhile`是在檢查為`true`把next event忽略掉檢查到`false`的時候開始放行。
例如,我們要收集字串,但我們要當字串是"start"的時候才開始收集字串,例:
```kotlin
val subscriptions \= CompositeDisposable()
subscriptions.add(
Observable.just("1", "2", "3", "Start", "1", "3", "2")
.skipWhile { it != "Start" } <-- 比對為false開始放行
.subscribe {
println("Get $it")
}
)
// Output
Get Start
Get 1
Get 3
Get 2
```
### `skipUntil`
與前面的skip operator不同`skipUntil`不是用lambda來決定skip的條件而是依賴於「另一個subject」`skipUntil`會一直忽略直到「另一個subject」發出`onNext` event。例
```kotlin
val subject = PublishSubject.create<String>()
val trigger = PublishSubject.create<String>()
subject
.skipUntil(trigger)
.subscribe {
println("it")
}
```
In this code you'll get nothing, until `trigger` sent an `onNext()` event.
Example:
```kotlin
subject.onNext("A") // Ignored
subject.onNext("B") // Ignored
trigger.onNext("1") // TRIGGER!
subject.onNext("C") // send out
```
![[Pasted image 20210202155003.png]]
### `take`
`take`跟[[20200207 - Study RxKotlin#skip]]相反,`take`是接收「前n個」訊息之後全部忽略。
### `takeWhile`
`takeWhile`是用lambda當判斷條件當判斷為`true`的時候放行,一旦判斷為`false`,之後的所有訊息都會被忽略。
```kotlin
exampleOf("takeWhile") {
val subscriptions = CompositeDisposable()
subscriptions.add(
Observable.fromIterable(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1))
.takeWhile {
it < 5
}
.subscribe {
println("Get $it")
}
)
}
```
上例中判斷到5的時候`it < 5`為`false`所以之後的都會忽略即使最後那個1也是一樣被忽略。
`takeWhile`跟[[20200207 - Study RxKotlin#skipWhile]]一樣,但是行為相反。
### `takeUntil`
跟[[20200207 - Study RxKotlin#skipUntil]]相反的行為會一直接收訊息直到他依賴的subject發出訊息後停止。
### `distinctUntilChanged`
`distinctUntilChanged`用來過濾「連續且相同」的訊息,例如連續的"Hi"那麼就只會收到第1個"Hi",之後的都不會收到。但是一旦收到的訊息改變了,再次收到以前發過的訊息,只要它沒有跟前一筆相同,那麼就還是會接收。例:
```kotlin
val subscriptions = CompositeDisposable()
subscriptions.add(
Observable.just(5, 5, 3, 3, 1)
.distinctUntilChanged()
.subscribe {
println(it)
}
)
// Output
5
3
1
```
`distinctUntilChanged`預設用class的`equal()` method來判斷當然我們也可以給它一個lambda來當客製我們的條件被lambda判斷為`true`的話,該訊息就會被忽略:
```kotlin
val subscriptions = CompositeDisposable()
subscriptions.add(
Observable.just("ABC", "CCD", "FAG")
.distinctUntilChanged { first, second ->
first[second.length - 1] == second[0]
}
.subscribe {
println("Get $it")
}
)
```
上例中我們希望「第2個字串的開頭字母要是跟第1個字串的結尾字母一樣的話那麼就不要顯示」。第1筆"ABC"一定會接收第2筆"CCD"則會被忽略第3筆"FAG"會被接收。
## 2. Transforming Operators
### 1. toList
`toList`可以把每一個單獨從Observalbe發出來的元素變成一個list
```kotlin
val subscriptions = CompositeDisposable()
val items = Observable.just("A", "B", "C")
subscriptions.add(
items.toList()
.subscribeBy {
println(it)
}
)
```
原本會單獨發出的"A", "B", "C"現在變成只翠發出一個List其內容是`["A", "B", "C"]`。
### 2. map
`map`跟Kotlin的`map`行為上差不多只是Kotlin的`map`是作用在List上而RxJava的`map`是作用在Observable上。`map`根據你提供的lambda函式來對每一個element做轉換如下例將每一個羅馬數字轉換為阿拉伯數字
```kotlin
val subscriptions = CompositeDisposable()
subscriptions.add(
Observable.just("M", "C", "V", "I")
.map {
it.romanNumeralIntValue()
}
.subscribeBy {
println(it)
}
)
```
注意到了嗎?`map`用來轉換Observable所包含的item型別上例中Observable的item本來是一個字串`String`),被`map`轉換為數字(`Int`)。
### 3. flatMap
`flatMap`用來處理「Observable發出來的Observable」並且「記錄每一個變化」。用例子比較好說明
```kotlin
class Student(val score: BehaviorSubject<Int>)
val subscriptions = CompositeDisposable()
val ryan = Student(BehaviorSubject.createDefault(80))
val charlotte = Student(BehaviorSubject.createDefault(90))
val student = PublishSubject.create<Student>()
student
.flatMap {
it.score
}
.subscribeBy {
println(it)
}
.addTo(subscriptions)
student.onNext(ryan) <-- 1
ryan.score.onNext(85) <-- 2
student.onNext(charlotte) <-- 3
ryan.score.onNext(95) <-- 4
charlotte.score.onNext(100) <-- 5
```
我們有一個叫做student的`PublishSubject`另外有兩個Student class分別是ryan與charlotte而這個Student class有一個member叫做scorescore的類別是`BehaviorSubject<Int>`。我們的`flatMap` lambda不做任何轉換直接bypass分數。
1. student先選擇ryan來發出第一個onNext eventryan原本的分數是80所以我們會收到80。
2. ryan變更為85所以我們會收到85。
3. student選擇了charlotte並發出一個onNext eventcharlotte原本的分數是90所以我們會收到90。
4. ryan變更為95所以我們會收到95。
5. charlotte變更為100所以我們會收到100。
結果會收到80、85、90、95、100。
ryan跟charlotte都是獨立的Observable但透過`flatMap`我們可以把它們的值(以及後續的變化)變成一連串的數值,這就是`flat`的意思。
![[rxJava_flatMap.png]]
### 4. switchMap
`switchMap`跟`flatMap`類似也是處理「Observable發出來的Observable」但是差別在於`switchMap`一但切換到新的Observable之後上一個Observale的訊息就部會收到了以`flatMap`的例子來說,在`student.onNext(charlotte)`這一行之後ryan的改變就不會收到了。例
```kotlin
val ryan = Student(BehaviorSubject.createDefault(80))
val charlotte = Student(BehaviorSubject.createDefault(90))
val student = PublishSubject.create<Student>()
student
.switchMap {
it.score
}
.subscribe {
println(it)
}
student.onNext(ryan)
ryan.score.onNext(85)
student.onNext(charlotte)
ryan.score.onNext(95)
charlotte.score.onNext(100)
```
結果會收到80、85、95、100。`ryan.score.onNext(95)`這一行的95不會收到。
`switchMap`適合用在會「改變興趣」的場景例如說原本是要持續收到台北氣溫的改變接著使用者把地點改到高雄那我們就會變成持續收到高雄的溫度變化而不是台北的又或者說你會隨著使用的的輸入持續的搜尋結果例如使用者依序輸入k、o、t、l、i、n每輸入一個字母我們就搜尋一次但我們只關注最後一個字串搜尋不在意之前的搜尋結果。
### 5. materialize
`materialize`能將Observable的值包裝成一個`Notification`,回到[[20200207 - Study RxKotlin#4 switchMap]]的例子,如果任何一個學生發出了`onError`的訊息,那麼連`student`本身都會因為這個Exception而中斷所以即使切到了charlotte我們也收不到charlotte的訊息了。`materialize`可以將`onError`包裝成一個`Notification`讓exception留在ryan本身而不會影響到上面的student。
```kotlin
val subscriptions = CompositeDisposable()
val ryan = Student(BehaviorSubject.createDefault(80))
val charlotte = Student(BehaviorSubject.createDefault(90))
val student = BehaviorSubject.createDefault<Student>(ryan)
// 1
val studentScore = student.switchMap { it.score.materialize() } <-- HERE!
// 2
subscriptions.add(
studentScore
.subscribe {
println(it)
}
)
// 3
ryan.score.onNext(85)
ryan.score.onError(RuntimeException("Error!"))
ryan.score.onNext(90)
// 4
student.onNext(charlotte)
```
![[rxkotlin_materialize.png]]
### 6. dematerialize
`dematerialize`用來反解`materialize`所包裝的東西,例如上例中,會將`materialize`所包裝出來的`Observable<Notification<Int!>!>!`反解為`Observable<Int>!`,例:
```kotlin
subscriptions.add(
studentScore
.filter {
if (it.error != null) {
println("Got error: ${it.error}")
false
} else {
true
}
}
.dematerialize { it }
.subscribe {
println(it)
}
)
```
要注意的是如果發生Exception的話直接`println`還是會產生Exception所以需要用`filter`來把error給濾掉。
![[rxkotlin_dematerialize.png]]
在Subject所發出的element仍然是Subject的時候如果element發生errorException會導致上層的Subject也跟著停止`materialize`/`dematerialize`可以用來包裝element讓element所發出的東西都變成`Notification`這樣就部會影響上層的Subject了。
## 3. Combining Operators
### startWith
用來在Observable本身所帶的item前面再加上其他item。實際的有`startWithIterable()`與`startWithItem()`。
例:
```kotlin
val subscriptions = CompositeDisposable()
val numbers = Observable.just(3, 4, 5)
val startWith = numbers.startWithIterable(listOf(1, 2))
startWith.subscribe {
println(it)
}.addTo(subscriptions)
```
### concat
`concat`是一個static method用來合併2個Observable。`concat`會先等第一個Observable結束然後再等待第二個之後把它們合併起來。
```kotlin
val subscriptions = CompositeDisposable()
val first = Observable.just(1, 2, 3)
val second = Observable.just(3, 4, 5)
Observable.concat(first, second)
.subscribe {
println(it)
}
.addTo(subscriptions)
```
### concatWith
跟[[20200207 - Study RxKotlin#concat]]一樣,但是`concatWith`是一個member function而不是一個static method。`concatWith`一樣會先等自己結束然後再等第二個Obervable當參數的那一個結束之後再合併。
```kotlin
val subscriptions = CompositeDisposable()
val first = Observable.just(1, 2, 3)
val second = Observable.just(3, 4, 5)
first.concatWith(second)
.subscribe {
println(it)
}
.addTo(subscriptions)
```
> ## 注意
> 要被合併的兩個Observable類型必須要一樣不可以一個是`Obsrvable<String>`而另一個是`Observable<Int>`compiler會報錯喔。
### concatMap
`concatMap`接受一個lambda函示並回傳另一個Observable序列`concatMap`會保證Observable的順序。
Given multiple Observable, and map each Observable to a lambda function. And make sure the sequence of given Observable list.
### merge
`merge`是一個static function。
`merge`會按照接收的順序把element合併起來
```kotlin
val subscriptions = CompositeDisposable()
val odd = PublishSubject.create<Int\>()
val even = PublishSubject.create<Int\>()
Observable.merge(odd, even)
.subscribe {
println(it)
}
.addTo(subscriptions)
odd.onNext(1)
even.onNext(2)
odd.onNext(3)
even.onNext(4)
odd.onNext(5)
even.onNext(6)
```
odd與even兩個交互發出elemetmerge依順序接收而不是像[[20200207 - Study RxKotlin#concat]]是依照Observable的順序。
`merge` complete的時機點定義如下
- 當來源的Observable與內部的Observable都complete的時候merge本身也會發出complete。
- 內部Observable結束的順序跟接收的順序沒有關係。一律按照接收點
- 如果有任何Observable發生error`merge`會轉發這個error然後自己發生terminate。
Q:
- What's the different between `flatMap()`? -> 很大的差別,`flatMap()` 有map的功能。
### mergeWith
就像`concatWith()`與`concat()`的關係,`mergeWith()`跟`merge`也是一樣的關係。
`mergeWith()`是一個member function必須由某個Observable instance來呼叫。
### combineLatest
combineLatest是`Observables`的static funtion注意不是`Observable`)。
combineLatest只會接收2個Observalbe的「最後一個」elements然後交由你所提供的lambda來處置
```kotlin
val subscriptions = CompositeDisposable()
val left = PublishSubject.create<String>()
val right = PublishSubject.create<String>()
Observables.combineLatest(left, right) { leftString, rightString ->
"$leftString, $rightString"
}.subscribe {
println(it)
}.addTo(subscriptions)
left.onNext("Hello")
right.onNext("World")
left.onNext("It's nice to")
right.onNext("be here")
left.onNext("Actually, it's super great to")
```
重點:
1. 在上例中是直接結合2個字串但是其實可以是任何用途。
2. 在實務中,`combineLatest`可以用來結合2個不同型別的Observable然後再回傳另一個不同型別的Observable。`combineLatest`回傳的Observable型別由lambda決定。
3. `combineLatest`必須在「每一個」Observable都發出element之後才會動作。如果不確定Observable是否會發出element可以使用[[20200207 - Study RxKotlin#startWith]]來讓Observable有一個初始值這樣可以避免`combineLatest`永遠不會發生的情況。
4. 如果有某個Observable已經complete`combineLatest`會保留它的最後一個element然後繼續結合更新的element。
5. 直到最後一個Observable complete`combineLatest`才會complete。
![[combineLatest.png]]
### zip
- `zip` wait until each if the inner Ovservables emits a new value.
### Triggers
#### withLastestFrom
- `withLatestFrom` is useful in all situations where you want the current(latest) value emittted frim an Observable, but only when a particular trigger occurs.
#### sample
- Just like `withLastFrom`. But each time the trygger Ivsercable emits a value, `sample` emits the latest value from the "other" Obervable, but only if it arrived since the last "tick". You can combine `withLastFrom` and `distinctUntilChanged` to do the same behavior of `sample`.
```
exampleOf("sample") {
val subscriptions = CompositeDisposable()
val button = PublishSubject.create<Unit>()
val editText = PublishSubject.create<String>()
editText.sample(button)
.subscribe {
println(it)
}.addTo(subscriptions)
editText.onNext("Par")
editText.onNext("Pari")
editText.onNext("Paris")
button.onNext(Unit)
button.onNext(Unit) <- button emits twice, but editText only emit last value
}
// Output
--- Example of: sample ---
Paris
```
### Switchs
- ambWith
- Think of `amb` as in ambiguous.
- `ambWith` connect to two Observables. And wait any of them who emit element first. If any Observable emit element, another one will be unsubscribed.
### reduce
- `reduce` accumulates a summary value.
### scan
- Like `reduce`, but emit per input value.
## 4. Time-Based Operators
### Buffering
#### replay
- This operator creates a new sequence that records the last N elements emitted by the source Observable.
#### replayAll
#### window
- Difference is that it emits an Observable of the buffered items, instead of emitting an array.
### Time-Shifting
#### delaySubscription
- Delay the time a subscriber starts receiving elements from its subscription.
#### delay
- This operator subscribes immediateley to the source observable, but delays every emitted element by the specified amount of time.
### Timer
#### Observable.interval
- Produce an infinite Observable sequence of Int values.
#### Observable.timer
- Specify a "due time" as the time that elapsed between the point of subscription and the first emitted value.
- If the "repeat period" is not assigned, the timer Observable will emit once, the complete.
#### timeout
- Emit an TimeoutException error event. If not caught, it terminates the sequence.
## 5. Explore Operators
# 參考資料:
- [RxMarbles: Interactive diagrams of Rx Observables](https://rxmarbles.com/#delayWhen)

View File

@@ -0,0 +1,22 @@
```bash
sudo apt update; sudo apt install -y cifs-utils lm-sensors
```
1. 安裝必須的套件
2. 修正時區
3. `mkdir ~/log`
4. 掛載 smb
5. 掛載 lvm
6. 設定 journalctl size
7. 設定 crontab
9. 啟動所有 docker
10. 掛載mod [[開機自動掛載模組modprobe nct6683]]
11. 設定 `sudo sensors-detect`
```shell
sudo apt update; sudo apt install -y cifs-utils lm-sensors
sudo timedatectl set-timezone Asia/Taipei
mkdir ~/log
```

172
21.01. Linux/CLI/cut.md Normal file
View File

@@ -0,0 +1,172 @@
## cut
Linux 的 `cut` 指令是一個實用的文字處理工具,可以將每一行文字的部份字元或欄位擷取出來,以下是使用方式與範例。
### 擷取字元
對於欄位寬度是固定的資料,可以使用擷取固定位置字元的方式,把指定的欄位抓出來,典型的例子就是從 `ls` 指令的輸出中擷取檔案權限。假設我們的 `ls` 指令與輸出資料如下:
```bash
# 僅輸出最後 5 筆檔案資訊
ls -l | tail -n 5
```
```
drwxr-xr-x 2 gtwang gtwang 4096 11月 7 22:29 影片
drwxr-xr-x 7 gtwang gtwang 4096 2月 6 21:01 文件
drwxr-xr-x 4 gtwang gtwang 4096 2月 22 21:09 桌面
drwxr-xr-x 2 gtwang gtwang 4096 1月 6 2017 模板
drwxrwxr-x 2 gtwang gtwang 4096 1月 6 2017 音樂
```
如果我們想要擷取第一欄中的檔案權限(也就是第 2 個字元到第 10 個字元),可以使用 `cut` 指令配合 `-c` 參數,將每一行的第 2 個字元至第 10 個字元抓出來:
```bash
# 擷取第 2 個字元至第 10 個字元
ls -l | tail -n 5 | cut -c 2-10
```
Output:
```
rwxr-xr-x
rwxr-xr-x
rwxr-xr-x
rwxr-xr-x
rwxrwxr-x
```
如果要擷取多個不連續的的區段,逗號分隔每個區段,例如:
```bash
# 擷取第 2-3 個、第 5-6 個與第 8-9 個字元
ls -l | tail -n 5 | cut -c 2-3,5-6,8-9
```
Output:
```
rwr-r-
rwr-r-
rwr-r-
rwr-r-
rwrwr-
```
### 排除字元
上面的範例中我們都是設定要擷取的部份,如果想要設定排除的部份,可以加上 `--complement` 這個補集參數,這樣 `cut` 就會將指定的部份刪除,留下剩餘的部份:
```bash
# 排除第 2 個字元至第 10 個字元
ls -l | tail -n 5 | cut -c 2-10 --complement
```
Output:
```
d 2 gtwang gtwang 4096 11月 7 22:29 影片
d 7 gtwang gtwang 4096 2月 6 21:01 文件
d 4 gtwang gtwang 4096 2月 22 21:09 桌面
d 2 gtwang gtwang 4096 1月 6 2017 模板
d 2 gtwang gtwang 4096 1月 6 2017 音樂
```
### 擷取欄位
若我們的資料欄位寬度不是固定的而是使用特定的字元分隔不同的欄位例如逗點分隔檔csv 檔):
```
5.1,3.5,1.4,0.2,"setosa"
4.9,3,1.4,0.2,"setosa"
7,3.2,4.7,1.4,"versicolor"
6.4,3.2,4.5,1.5,"versicolor"
5.9,3,5.1,1.8,"virginica"
```
若要擷取這個 csv 檔的特定欄位,可以使用 `cut` 指令加上 `-d` 參數指定欄位分隔字元,並以 `-f` 參數指定欲擷取的欄位,例如擷取出第 2 個欄位:
```bash
# 擷取 CSV 檔的第二個欄位
cut -d , -f 2 data.csv
```
Output:
```
3.5
3
3.2
3.2
3
```
若要擷取多個欄位,也是使用逗號分隔每個欄位:
```bash
# 擷取 CSV 檔的第 1-3 個與第 5 個欄位
cut -d , -f 1-3,5 data.csv
```
```
5.1,3.5,1.4,"setosa"
4.9,3,1.4,"setosa"
7,3.2,4.7,"versicolor"
6.4,3.2,4.5,"versicolor"
5.9,3,5.1,"virginica"
```
Linux 中的 `/etc/passwd` 檔案內容是以冒號分隔欄位的,若要從中擷取特定的欄位,可以指定以冒號為分隔字元:
```bash
# 擷取 /etc/passwd 的第 1 個與第 7 個欄位
head -n 5 /etc/passwd | cut -d : -f 1,7
```
```
root:/bin/bash
daemon:/usr/sbin/nologin
bin:/usr/sbin/nologin
sys:/usr/sbin/nologin
sync:/bin/sync
```
### 排除欄位
若要排除某些特定欄位,而留下其餘的欄位,同樣可以使用 `--complement` 參數:
```bash
# 排除 CSV 檔的第二個欄位
cut -d , -f 2 --complement data.csv
```
```
5.1,1.4,0.2,"setosa"
4.9,1.4,0.2,"setosa"
7,4.7,1.4,"versicolor"
6.4,4.5,1.5,"versicolor"
5.9,5.1,1.8,"virginica"
```
### 輸出分隔字元
`cut` 在輸出多欄位的資料時,預設會以輸入檔案所使用的分隔字元來分隔輸出的欄位,若要改變輸出欄位的分隔字元,可以使用 `--output-delimiter` 參數來指定:
```bash
# 指定輸出欄位分隔字元
head -n 5 /etc/passwd | cut -d : -f 1,7 --output-delimiter="^_^"
```
```
root^_^/bin/bash
daemon^_^/usr/sbin/nologin
bin^_^/usr/sbin/nologin
sys^_^/usr/sbin/nologin
sync^_^/bin/sync
```
### 實用範例
Linux 的系統管理者時常會需要使用 `ps` 指令查看各行程的狀態,但由於 `ps` 的輸出資訊很多,如果我們只想看程式的 PID 與指令內容,就可以用 `cut` 把要用的資訊擷取來:
```bash
# 找出所有 Python 程式的 PID 與指令內容
ps aux | grep python | sed 's/\s\+/ /g' | cut -d ' ' -f 2,11-
```
```
17100 grep --color=auto python
27904 /usr/bin/python -Es /usr/sbin/tuned -l -P
33890 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
```

57
21.01. Linux/CLI/scp.md Normal file
View File

@@ -0,0 +1,57 @@
`scp` 指令的語法跟一般的 `cp` 類似,只不過 `scp` 可以在不同的 Linux 主機之間複製檔案,其語法為:
```
scp [帳號@來源主機]:來源檔案 [帳號@目的主機]:目的檔案
```
# 保留檔案時間與權限
若要讓檔案在複製之後,還可保留原本的修改時間、存取時間與權限,可以加上 `-p` 參數:
```
scp -p /path/file1 myuser@192.168.0.1:/path/file2
```
# 資料壓縮
若要將資料壓縮之後再傳送,減少網路頻寬的使用量,可以加上 `-C` 參數:
```
scp -C /path/file1 myuser@192.168.0.1:/path/file2
```
# 限制傳輸速度
若要限制網路的使用頻寬,可以用 `-l` 指定可用的網路頻寬上限值(單位為 Kbit/s
```
# 限制傳輸速度為 400 Kbit/s
scp -l 400 /path/file1 myuser@192.168.0.1:/path/file2
```
這樣就會限制 `scp` 只能使用 `400` Kbit/s也就是 `400 / 8 = 50` KB/s。
# 自訂連接埠
一般 SSH 伺服器的連接埠號為 22如果遇到使用非標準埠號的伺服器可以用 `-P` 來指定埠號。若遠端的 SSH 伺服器使用 `2222` 這個連接埠,我們就可以這樣複製檔案:
```
# 使用 2222 連接埠
scp -P 2222 /path/file1 myuser@192.168.0.1:/path/file2
```
# IPv4 與 IPv6
`-4``-6` 兩個參數分別可以讓 `scp` 使用 IPv4 與 IPv6 來傳輸資料:
```
# 使用 IPv4
scp -4 /path/file1 myuser@192.168.0.1:/path/file2
# 使用 IPv6
scp -6 /path/file1 myuser@192.168.0.1:/path/file2
```
# 更快的方法使用SSH+TAR
```
ssh 使用者@主機 "cd 目標目錄 ;tar -zcvf - 目標" | cat > 目標.tar.gz
```
例:
```
ssh 192.168.0.22 "cd /var ;tar -zcvf - log" | cat > 22_log.tar.gz
```
# 參考
- [SSH + TAR 取代 SCP @ Vexed's Blog :: 隨意窩 Xuite日誌](https://blog.xuite.net/vexed/tech/586811949)
- [Linux中互传文件ssh+tar 与Scp 比较 - 简书](https://www.jianshu.com/p/856a2dc883e0)
- [轉貼--ssh tar 命令把遠端檔拉回來或推過去 --- 山城風雲的點滴](http://jimsung168.blogspot.com/2014/01/ssh-tar.html)

View File

@@ -0,0 +1,4 @@
## 列出系統中的 systemd service
```bash
systemctl list-units --type=service
```

View File

@@ -0,0 +1,14 @@
`timedatectl`可以看現在的時間、時區等等。也可以改變時區。
![[Pasted image 20220516125616.png]]
`timedatectl list-timezones`列出所有時區我們已經知道我們的時區是Taipei所以可以用grep直接把它抓出來
```bash
timedatectl list-timezones | grep -i taipei
```
結果如下:
![[Pasted image 20220516125742.png]]
設定時區:
```
timedatectl set-timezone Asia/Taipei
```

42
21.01. Linux/Docker.md Normal file
View File

@@ -0,0 +1,42 @@
# 安裝
根據[# Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/)來安裝Docker engine。
# 指令
## 列出可用的版本
```bash
# List the available versions:
apt-cache madison docker-ce | awk '{ print $3 }'
5:24.0.0-1~debian.11~bullseye
5:23.0.6-1~debian.11~bullseye
...
```
## 列出 container
`docker ps`會列出執行中的container但是停止的不會
```bash
sudo docker ps
```
如果也要列出已停止的container
```bash
sudo docker ps -a
```
## 刪除 container
Container必須是停止狀態才可以刪除
```bash
sudo docker rm <CONTAINER_ID>
```
## 進入 container 執行 bash
```bash
docker run -i -t <CONTAINER_NAME> /bin/bash
```
## 列出 image
```bash
sudo docker image ls
or
sudo docker images
```

37
21.01. Linux/NUT.md Normal file
View File

@@ -0,0 +1,37 @@
---
tags:
aliases:
date: 2025-01-07
time: 22:52:59
description:
---
在啟用 NAS 的 UPS server 之後,可以安裝 `nut-client` 來接收 NAS 的通知以進行關機。
## 安裝套件
```bash
sudo apt install nut-client
```
## 測試
```bash
upsc ups@192.168.1.11
```
## 設定
```bash
sudo vim /etc/nut/upsmon.conf
```
加入 `MODE=netclient`
```bash
sudo vim /etc/nut/upsmon.conf
```
加入 `MONITOR ups@192.168.1.11 1 monuser secret slave`
# 參考來源
- [Linux 下使用 NUT 实现断电自动关机 | varkai](https://varkai.com/posts/operation/linux-uses-nut-to-realize-automatic-poweroff/)
- [ups - Use Synology as NUT server for Ubuntu Server 20.04 - Ask Ubuntu](https://askubuntu.com/questions/1244064/use-synology-as-nut-server-for-ubuntu-server-20-04)
- [UPS 低电压时通过群晖实现 Ubuntu 自动关机 - 杜老师说](https://dusays.com/557/)

109
21.01. Linux/Watchtower.md Normal file
View File

@@ -0,0 +1,109 @@
## 安裝 Watchtower
```shell
docker pull Watchtower
```
## 使用方法
```shell
docker run --detach \
--name watchtower \
--volume /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
```
使用剛剛的方法會拉取最新鏡像,但並不會自動刪除舊有鏡像,時間一長就會佔用很大空間,這裡可以使用 `--cleanup` 選項,在更新完舊容器之後自動刪除舊鏡像
```shell
docker run -d \
--name watchtower \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup
```
### 或使用 docke-compose.yml
```yaml
version: "3"
services:
watchtower:
image: containrrr/watchtower
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 86400 --cleanup
```
### 設置自動更新檢測頻率
Watchtower 默認是每 5min 檢測一次,如果需要更改週期,可以使用 `--interval``-i` 選項
如設定每小時檢測一次:
```shell
docker run -d \
--name watchtower \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup \
-i 3600
```
`-i 3600`:這裡的 `3600` 是設置的時間週期,單位是**秒** `3600` 即 1 小時
### 指定更新檢測時間
除了設置頻率,還可以使用 `--schedule``-s` 選項指定時間 如指定每天 UTC+8 時間凌晨 3 點更新。
```shell
docker run -d \
--name watchtower \
--restart always \
-e TZ=Asia/Taipei \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup \
-s "0 0 3 * * *"
```
這裡的執行時間為 UTC 時間,如果不指定時區,會比台北的 UTC+8:00 時間晚 8 小時
### 制定需要更新的容器
如果不想更新所有容器,可以設置指定的容器進行更新
`nginx``netdata` 這兩個容器舉例:
```shell
docker run -d \
--name watchtower \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup \
nginx netdata
```
這裡指定的容器只需要填寫**容器名**,而不是填寫該容器的**鏡像名**
## 手動更新
如果不想在使用容器時被自動更新打斷,可以使用 Watchtower 進行手動更新
由於是手動更新Watchtower 只需要用到一次,可以添加 `--rm``--run-once` 參數,在更新完之後過河拆橋
### 手動更新所有容器
```shell
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup \
--run-once
```
## 手動更新指定容器
繼續以 `nginx``netdata` 這兩個容器舉例
```shell
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--cleanup \
--run-once \
nginx netdata
```
## 參考
- [使用Watchtower自動更新Docker鏡像和容器 - Docker容器 - 計算機 | 🧂 = 鹽🧂的記事本 = ( ´∀`)ヵヵヵ](https://www.sio.moe/2021/12/18/computer/Docker-Container/Use-Watchtower-to-automatically-update-Docker-images-and-containers/)
- [storjlabs/watchtower Tags | Docker Hub](https://hub.docker.com/r/storjlabs/watchtower/tags)

29
21.01. Linux/_Map.canvas Normal file
View File

@@ -0,0 +1,29 @@
{
"nodes":[
{"id":"2387a7bafd0d1fa1","type":"text","text":"Linux","x":-60,"y":-60,"width":112,"height":60,"color":"1"},
{"id":"480671b59b65fbee","type":"text","text":"網路","x":-260,"y":-200,"width":123,"height":60},
{"id":"0da49f9f13cb5ea0","type":"text","text":"虛擬化","x":140,"y":-200,"width":125,"height":60},
{"id":"67febece2a817d3a","type":"text","text":"系統管理","x":140,"y":80,"width":125,"height":60},
{"id":"c30ec48841c09729","type":"text","text":"# LOG 管理\n[[journalctl]]","x":341,"y":80,"width":186,"height":100},
{"id":"89e25cea38eee1c6","type":"text","text":"# 定時操作\n[[crontab]]","x":347,"y":220,"width":180,"height":100},
{"id":"0211a8b636cb3542","type":"text","text":"指令操作","x":-262,"y":80,"width":125,"height":60},
{"id":"584c8fe580aa5c65","type":"text","text":"- [[cut]]\n- [[scp]]\n- [[timedatectl]]\n- [[systemd]]","x":-620,"y":55,"width":250,"height":110},
{"id":"5396ff224cf2433d","type":"text","text":"- [[Apache]]\n- [[Gitea]]\n- [[Grafana]]\n- [[Nextcloud]]\n- [[Pelican blog]]\n- [[Proxmox VE]]\n- [[Storj]]\n- [[filebrowser]]\n- [[freshrss]]\n- [[Grafana]]","x":-700,"y":-577,"width":242,"height":277},
{"id":"631ba79f09378fa8","type":"text","text":"![[Docker|Docker]]","x":312,"y":-299,"width":348,"height":219},
{"id":"d5901d4a7879a984","type":"text","text":"架站","x":-432,"y":-468,"width":125,"height":60},
{"id":"8b9a102062abf2ea","type":"text","text":"![[smb client]]","x":-699,"y":-231,"width":330,"height":201}
],
"edges":[
{"id":"2621855f17f1fce7","fromNode":"2387a7bafd0d1fa1","fromSide":"right","toNode":"0da49f9f13cb5ea0","toSide":"left"},
{"id":"63fe0dc1ef02bcf0","fromNode":"2387a7bafd0d1fa1","fromSide":"right","toNode":"67febece2a817d3a","toSide":"left"},
{"id":"8b1293fe476ce994","fromNode":"2387a7bafd0d1fa1","fromSide":"left","toNode":"480671b59b65fbee","toSide":"right"},
{"id":"7b14c2274a6a7dce","fromNode":"480671b59b65fbee","fromSide":"left","toNode":"8b9a102062abf2ea","toSide":"right"},
{"id":"3fb7bd17056728f2","fromNode":"0da49f9f13cb5ea0","fromSide":"right","toNode":"631ba79f09378fa8","toSide":"left"},
{"id":"47dcc8ff2aa219d2","fromNode":"67febece2a817d3a","fromSide":"right","toNode":"c30ec48841c09729","toSide":"left"},
{"id":"b9cbf23293526def","fromNode":"67febece2a817d3a","fromSide":"right","toNode":"89e25cea38eee1c6","toSide":"left"},
{"id":"305a94a3648fc58b","fromNode":"d5901d4a7879a984","fromSide":"left","toNode":"5396ff224cf2433d","toSide":"right"},
{"id":"1007d8512d956cdb","fromNode":"480671b59b65fbee","fromSide":"left","toNode":"d5901d4a7879a984","toSide":"right"},
{"id":"c6875871d1de0864","fromNode":"2387a7bafd0d1fa1","fromSide":"left","toNode":"0211a8b636cb3542","toSide":"right"},
{"id":"40f0ca450747f830","fromNode":"0211a8b636cb3542","fromSide":"left","toNode":"584c8fe580aa5c65","toSide":"right"}
]
}

84
21.01. Linux/crontab.md Normal file
View File

@@ -0,0 +1,84 @@
### 加入
使用`crontab -e`,然後加入這一行:
`*/1 * * * * /home/awin/script/ddns.sh`
### 說明
![[Pasted image 20240111231507.png]]
依序是 **分鐘,   小時,   日期,   月份,    星期,   command**
參數為 0-59,   0-23,   1-31,  1-21,   0-6,   需要執行的command
**※ 星期參數為 0 代表星期日**
- 【*】:星號,代表任何時刻都接受的意思
- 【,】逗號代表分隔時段。例如30 9,17 * * * command代表早上 9 點半和下午五點半都執行 command。
- 【-】減號代表一段時間範圍。例如15 9-12 * * * command代表從 9 點到 12 點的每個 15 分都執行 command。
- 【/n】斜線n 代表數字,表示每個 n 單位間隔。例如:*/5 * * * * command代表每隔 5 分鐘執行一次 command。
還有一些人性化的參數,一次取代全部五個數字參數
-@reboot】 :僅在開機的時候執行一次。
-@yearly 一年執行一次和0 0 1 1 * command效果一樣。
-@annually】:(和@yearly一樣
-@monthly一個月執行一次和0 0 1 * * command效果一樣。
-@weekly一個星期執行一次和0 0 * * 0 command效果一樣。
-@daily每天執行和0 0 * * * command效果一樣。
-@midnight】:(和@daily一樣
-@hourly 每小時執行和0 * * * * command效果一樣。
### 範例
- 每 5 分鐘執行一次:
- `*/5 * * * *  root    /usr/libexec/atrun`
- 每 5 小時執行一次
- `* */5 * * * root    /usr/libexec/atrun`
- 每天 AM 5:00 執行指令(星號與星號之間要有空隔)
- `00 05   * * *  username /bin/bash /路徑/command`
- 1 至 20 號每天執行一次
- `0 1 1-20  * * root    /usr/libexec/atrun`
- 當分針移到第 5 分時,執行此 cron
- `5 * * * * root    /usr/libexec/atrun`
- 當時針移到 1 點 1 分時,執行此 cron
- `1 1 * * * root    /usr/libexec/atrun`
- 每週一的 1 點 1 分,執行
- `1 1 * * 1 root    /usr/libexec/atrun`
- 2 月 29 日時針到 1 點 1 分,執行
- `1 1 29 2 * root    /usr/libexec/atrun`
- 8 點到 16 點每 5 分鐘執行一次 cron
- `/5 8-16 * * *   root    /usr/libexec/atrun`
### crontab 命令
#### 啟動
`sudo service cron start`
#### 重新啟動
`/etc/init.d/cron restart`
#### 查詢service狀態
`sudo /etc/init.d/cron status`
#### 編輯 crontab
`crontab -e`
### `awin` 的 crontab
```
# m h day mon weekday command
*/5 * * * * /home/awin/script/clean_nextcloud.sh
```
### `root` 的 crontab
```
# m h day mon dow command
*/3 * * * * /home/awin/script/ddns.sh > /home/awin/log/ddns.log 2>&1
*/5 * * * * /home/awin/script/clean_nextcloud.sh > /home/awin/log/clean_nextcloud.log 2>&1
1 2 * * 1 /home/awin/script/backup_adguardhome.sh > /home/awin/log/backup_docker_adguardhome.log 2>&1
2 2 * * 1 /home/awin/script/backup_filebrowser.sh > /home/awin/log/backup_docker_filebrowser.log 2>&1
3 2 * * 1 /home/awin/script/backup_freshrss.sh > /home/awin/log/backup_docker_freshrss.log 2>&1
4 2 * * 1 /home/awin/script/backup_gitea.sh > /home/awin/log/backup_docker_gitea.log 2>&1
5 2 * * 1 /home/awin/script/backup_immich.sh > /home/awin/log/backup_docker_immich.log 2>&1
1 2 * * 2 /home/awin/script/backup_nextcloud.sh > /home/awin/log/backup_docker_nextcloud.log 2>&1
* * 1 * * /home/awin/script/backup_dotfiles_awin-pc2.sh > /home/awin/log/backup_dotfiles_awin-pc2.log 2>&1
1 1 * * * /home/awin/script/rsync_nasphoto.sh > /home/awin/log/rsync_nasphoto.log 2>&1
@reboot /home/awin/script/reboot_echo.sh > /home/awin/log/reboot.log 2>&1
## Restart Docker containers
# m h day mon dow command
0 4 * * * docker restart grafana-prometheus-local_data_exporter-1 > /home/awin/log/restart_grafana-prometheus-local_data_exporter-1.log 2>&1
```

38
21.01. Linux/fstab.md Normal file
View File

@@ -0,0 +1,38 @@
# 看磁碟UUID
```bash
sudo blkid
```
```result
/dev/mmcblk0p1: LABEL_FATBOOT="bootfs" LABEL="bootfs" UUID="62C9-51DB" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="bd53c125-01"
/dev/mmcblk0p2: LABEL="rootfs" UUID="2b2cb0b0-aa00-4bd0-b775-4abc1ff31eb2" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="bd53c125-02"
/dev/sda1: UUID="8bd933c6-2625-498f-84cf-0b1394aa3f5e" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="Linux filesystem" PARTUUID="6e7fd2d9-1c28-4570-aae2-022f5345781f"
```
其中,`UUID="XXXXXXX....."` 這一串就是硬碟的UUID複製下來等一下要填到 `fstab` 裡面。
# `/etc/fstab`
打開 `/etc/fstab`,加入一行:
```config
UUID=8bd933c6-2625-498f-84cf-0b1394aa3f5e /extusb1/storj ext4 defaults,nofail 0 0
```
- `UUID=8bd933c6-2625-498f-84cf-0b1394aa3f5e`:指定要掛載的磁區是那一個
- `/extusb1/storj`:指定要掛載的目錄
- `ext4`: 指定要掛載的格式
- `defaults,nofail`:檔案格式參數區。`nofail` 是說要是掛載失敗不要產生錯誤訊息,避免因為硬碟壞掉而卡在開機畫面,造成開不了機。尤其是當你的電腦沒有螢幕、鍵盤的時候,會很麻煩。
- `0`:是否可以被 dump 指令備份 (0不要做備份﹔1要做備份﹔2要做備份重要度比 1 小)。
- `0`:是否於開機時以 fsck 檢驗磁區 (0不檢驗﹔1先檢驗﹔2後檢驗)。如果是內接式的硬碟可以改成`0 1`
# AWIN-PC2 的 fstab
```
# <file system> <mount point> <type> <options> <dump> <pass>
/dev/disk/by-uuid/38cb80c1-504f-4b07-8a86-27e1c22b5deb / ext4 defaults 0 1
/dev/disk/by-uuid/A2D8-2DF1 /boot/efi vfat defaults 0 1
/swap.img none swap sw 0 0
UUID=be92f54f-e427-496e-83e9-a29643aa5af0 /lvm1 ext4 defaults,nofail 0 2
//192.168.1.11/share /media/share cifs credentials=/home/awin/.smbcredentials_shareview,defaults 0 0
//192.168.1.11/upload /media/upload cifs credentials=/home/awin/.smbcredentials_shareview,defaults 0 0
//192.168.1.11/Backup /media/backup cifs credentials=/home/awin/.smbcredentials_backup,defaults 0 0
//192.168.1.11/photo /media/nasphoto cifs credentials=/home/awin/.smbcredentials_photoMgr,defaults 0 0
```

14
21.01. Linux/iptable.md Normal file
View File

@@ -0,0 +1,14 @@
---
tags:
aliases:
date: 2024-07-30
time: 19:30:12
description:
---
## Allow all input
```
iptables -I INPUT -j ACCEPT
```
# 參考來源

129
21.01. Linux/journalctl.md Normal file
View File

@@ -0,0 +1,129 @@
## 看log
Raspberry Pi 4沒有`/var/log/syslog`,要使用`journalctl`
```bash
journalctl
```
Or
```bash
journalctl | grep SOMETHING
```
### 看系統訊息
```bash
journalctl -p 0
```
#### Error code意思
```
0: 紧急情况
1: 警报
2: 危急
3: 错误
4: 警告
5: 通知
6: 信息
7调试
```
### 看開機log
```bash
journalctl --list-boots
```
- 第一個數字顯示的是journald 的唯一的啟動追蹤號碼,你可以在下一個命令中使用它來分析該特定的啟動。
- 第二個數字是啟動ID你也可以在指令中指定。
- 接下來的兩個日期、時間組合是儲存在對應文件中的日誌的時間。如果你想找出某個特定日期、時間的日誌或錯誤,這就非常方便了。
例如:
```bash
journalctl -b -45
```
Or
```bash
journalctl -b 8bab42c7e82440f886a3f041a7c95b98
```
也可以使用 `-x` 選項在顯示器上加入systemd 錯誤訊息的解釋。在某些情況下,這是個救命稻草。例:
```bash
journalctl -xb -p 3
```
### 看某一特定時間、日期的日誌記錄
使用 `--since` 選項與 `yesterday``today``tomorrow` 或 `now` 組合。
以下是一些不同指令的範例。你可以根據你的需求修改它們。它們是不言自明的。以下命令中的日期、時間格式為 `"YYYY-MM-DD HH:MM:SS"`
```bash
journalctl --since "2020-12-04 06:00:00"
journalctl --since "2020-12-03" --until "2020-12-05 03:00:00"
journalctl --since yesterday
journalctl --since 09:00 --until "1 hour ago"
```
### 看內核特定的記錄
```bash
journalctl -k
```
### 過濾出某個systemd 服務單元的特定日誌
例如,如果要查看 NetworkManager 服務的日誌
```bash
journalctl -u NetworkManager.service
```
如果不知道service name看[[systemd#列出系統中的 systemd service]]
### 查看使用者、群組的日誌
```bash
id -u debugpoint # 先找出使用者的uid
journalctl _UID=1000 --since today
```
### 查看可執行檔的日誌
```bash
journalctl /usr/bin/gnome-shell --since today
```
### 看log佔用的磁碟空間
```bash
journalctl --disk-usage
```
### 清除日誌
#### 手動清除
```bash
sudo journalctl --flush --rotate # 將所有日誌歸檔
sudo journalctl --vacuum-time=7d # 只保留最近7天的日誌
sudo journalctl --vacuum-time=1s # 只保留最近1秒的日誌
```
或者設定日誌的大小
```bash
sudo journalctl --vacuum-size=400M # 保留最後400M
```
#### 自動清除
修改`/etc/systemd/journald.conf`,裡面有下面幾個設定項:
- `SystemMaxUse`: 指定日志在持久性存储中可使用的最大磁盘空间。例:`SystemMaxUse=500M`
- `SystemKeepFree`: 指定在将日志条目添加到持久性存储时,日志应留出的空间量。例:`SystemKeepFree=100M`
- `SystemMaxFileSize`: 控制单个日志文件在被轮换之前在持久性存储中可以增长到多大。例:`SystemMaxFileSize=100M`
- `RuntimeMaxUse`: 指定在易失性存储中可以使用的最大磁盘空间(在 /run 文件系统内)。例:`RuntimeMaxUse=100M`
- `RuntimeKeepFree`: 指定将数据写入易失性存储(在 /run 文件系统内)时为其他用途预留的空间数量。例:`RuntimeMaxUse=100M`
- `RuntimeMaxFileSize`: 指定单个日志文件在被轮换之前在易失性存储(在 /run 文件系统内)所能占用的空间量。例:`RuntimeMaxFileSize=200M`
修改後記得重啟 `journalctl`
![[journalctl#重啟日誌]]
也請記得[[journalctl#確認日誌的完整性]]
### 確認日誌的完整性
```bash
journalctl --verify
```
### 重啟日誌
若是有改變設定,記得重啟以讓變更生效:
```shell
sudo systemctl restart systemd-journald
```
### 參考
- [系統運作|如何使用journalctl 檢視和分析systemd 日誌(附實例)](https://linux.cn/article-15544-1.html)

42
21.01. Linux/lsblk.md Normal file
View File

@@ -0,0 +1,42 @@
`lsblk -o` 可以看到所有硬碟的詳細資料,但是資料廖很大,可以在騎之後加上指定的蘭未來縮減資料量,例如:
```bash
sudo lsblk -o NAME,MODEL,SERIAL,WWN,HCTL
```
```result
NAME MODEL SERIAL WWN HCTL
sda ST3000VN000-1H41 W300JG66 0x5000c50069c93ec4 0:0:0:0
└─sda1 0x5000c50069c93ec4
├─vg1-lv1_rmeta_0
│ └─vg1-lv1
└─vg1-lv1_rimage_0
└─vg1-lv1
sdb WDC WDS480G2G0A- 20418C804426 0x5001b448bb7f096a 0:0:1:0
├─sdb1 0x5001b448bb7f096a
├─sdb2 0x5001b448bb7f096a
├─sdb3 0x5001b448bb7f096a
└─sdb4 0x5001b448bb7f096a
sdc CT480BX500SSD1 2044E4C26B3C 0x500a0751e4c26b3c 0:0:2:0
├─sdc1 0x500a0751e4c26b3c
└─sdc2 0x500a0751e4c26b3c
sdd ST1000LM035-1RK1 WDEM0VBP 0x5000c500ab7d2dde 0:0:3:0
└─sdd1 0x5000c500ab7d2dde
sde ST3000VN000-1H41 W300JHNF 0x5000c50069c8d4b2 0:0:4:0
└─sde1 0x5000c50069c8d4b2
├─vg1-lv1_rmeta_1
│ └─vg1-lv1
└─vg1-lv1_rimage_1
└─vg1-lv1
sdf TOSHIBA HDWN180 57F4K126FP9E 0x50000397cb881edb 0:0:5:0
├─sdf1 0x50000397cb881edb
├─sdf2 0x50000397cb881edb
└─sdf5 0x50000397cb881edb
└─md127
sdg ST8000VN0022-2EL ZA19RA2H 0x5000c500a5e7e1bf 0:0:6:0
├─sdg1 0x5000c500a5e7e1bf
├─sdg2 0x5000c500a5e7e1bf
└─sdg5 0x5000c500a5e7e1bf
└─md127
nvme0n1 ADATA LEGEND 710 2N422LABN7CR eui.324e3432324c41424ce000184e374352
├─nvme0n1p1 eui.324e3432324c41424ce000184e374352
└─nvme0n1p2 eui.324e3432324c41424ce000184e374352
```

187
21.01. Linux/lvm.md Normal file
View File

@@ -0,0 +1,187 @@
# PV(Physical Volume)
PV(Physical Volume) 就是LVM的磁碟分區。
## 建立PV
建立PV的命令如下
```bash
pvcreate /dev/sdb /dev/sdc /dev/sdd
```
```result
`Physical volume "/dev/sdb" successfully created`
`Physical volume "/dev/sdc" successfully created`
`Physical volume "/dev/sdd" successfully created`
```
這樣我們就建立了3個PV。
## 查看 PV
使用 `pvdisplay` 和 `pvs` 來檢視你建立的 PV。
`pvs` 輸出會比較簡短,例:
```
sudo pvs
```
```result
PV VG Fmt Attr PSize PFree
/dev/sda3 ubuntu-vg lvm2 a-- 57.62g 28.81g
/dev/sdb1 vg1 lvm2 a-- <2.73t 0
/dev/sdc1 vg1 lvm2 a-- <2.73t 0
```
 `pvdisplay` 就比較詳細:
```
sudo pvdisplay 
```
```result
--- Physical volume ---
PV Name /dev/sdb1
VG Name vg1
PV Size <2.73 TiB / not usable 3.44 MiB
Allocatable yes (but full)
PE Size 4.00 MiB
Total PE 715396
Free PE 0
Allocated PE 715396
PV UUID cWvfBE-Vbyp-l09E-QH0O-ZZoC-AdSG-t1J7TT
--- Physical volume ---
PV Name /dev/sdc1
VG Name vg1
PV Size <2.73 TiB / not usable 3.00 MiB
Allocatable yes (but full)
PE Size 4.00 MiB
Total PE 715396
Free PE 0
Allocated PE 715396
PV UUID eDdYr4-HSZC-wRBa-feGx-SHp1-Wfye-m0e1PY
--- Physical volume ---
PV Name /dev/sda3
VG Name ubuntu-vg
PV Size 57.62 GiB / not usable 2.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE 14751
Free PE 7376
Allocated PE 7375
PV UUID zUlIaB-1Uof-3eF6-z5I7-OnqJ-UYDk-gThxJQ
```
# VG(Volume Group)
VG(Volume Group) 由 PV 組成你可以自由的排列組合。我們拿剛剛建立的3個PV把它組成一個VG。
## 建立 VG
```
vgcreate vg1 /dev/sdb /dev/sdc /dev/sdd
```
`vg1` 是你的VG 名字,可以自由更改。
後面的`/dev/sdb``/dev/sdc``/dev/sdd` 就是你剛剛建立的 PV。
## 查看 VG
使用 `vgdisplay` 和 `vgs` 來檢視你建立的 VG。
# LV(Logical Volume)
LV(Logical Volume) 可以對應到實際的硬碟,它才是可以被 mount 到 directory 的東西。LV 可以只使用 VG 某一部份的也就是說一個VG可以切出很多LV。
## 建立 LV
```
lvcreate --size 10G --name lv1 vg1
```
`--size 10G` 表示 LV 的空間是 10G
`--name lv1` 表示 LV的名字叫做 `lv1`
最後的 `vg1` 則是你要從那一個 VG 來建立這個 LV。
當然可以直接指定LV使用所有的空間
```
lvcreate -l +100%FREE --name lv1 vg1
```
`-l``--size` 同義。
### 建立 raid 1
使用 `--type raid1` 可以建立 raid 1 的 LVM
```
lvcreate --type raid1 -l +100%FREE --name lv1 vg1
```
## 查看 LV
使用 `lvdisplay` 和 `lvs` 來檢視你建立的 LV。
# 格式化 LV
建立好LV之後就可以格式化它然後掛載它先用`lvdisplay`確認一下 LV的路徑
![[20240228_170525_WindowsTerminal_901x169.png]]
格式化:
```
mkfs -t ext4 /dev/vg1/lv1
```
# Mount LV
先建立一個目錄來掛載 LV:
```
mkdir /lvm1
```
再把 LV 掛上去:
```
mount /dev/vg1/lv1 /lvm1
```
這樣就可以在 /myLVM 操作了。
## 開機自動掛載
![[開機自動掛載硬碟]]
# LVM 增加空間
增加空間的大概步驟是這樣:
1. 電腦裝上新硬碟
2. 建立PV如[[lvm#建立PV]]
3.`vgextend` 把這個新的 PV 加入到既有的 VG
4.`lvextend` 來擴大容量
5.`resize2fs` 來擴大容量
## 用 `vgextend` 新增 PV
假設 vg1 是目前的 VG 名字,新增的 PV是 /dev/sdc
```
vgextend vg1 /dev/sdc
```
## 用 `lvextend` 來擴大容量
先用`lvdisplay`確認一下 LV的路徑假設是 `/dev/vg1/lv1`
```
lvextend -L +10G /dev/vg1/lv1 # 多 10G 空間
or
lvextend -l +40%FREE /dev/vg1/lv1 多 40% 空間
```
## 用 `resize2fs` 來擴大容量
```
resize2fs /dev/vg1/lv1
```
# LVM 換電腦
可能你重灌,或是把硬碟從這一台電腦換到另一台電腦,這都需要重新 "active" 原本的 LVM。
先用 `lvscan` 看看是否有找到原本的 LVM
![[20240311_194022_WindowsTerminal_925x99.png]]
如果有的話,紅框位置就是 LVM 的路徑,之後只要啟用它就可以了:
```
sudo lvchange -a y /dev/vg1/lv1
```
之後再[[硬碟操作#掛載硬碟|mount]]它就好了。
# 參考
- [What is LVM2 in Linux ?. LVM](https://medium.com/@The_CodeConductor/what-is-lvm2-in-linux-3d28b479e250)
- [建立LVM磁區 - HackMD](https://hackmd.io/@yzai/BJUIhnAb9)
- [LVM — pv, vg, lv](https://sean22492249.medium.com/lvm-pv-vg-lv-1777a84a3ce8)
- [Linux LVM (建立、擴充、移除LVM磁區) 操作筆記](https://sc8log.blogspot.com/2017/03/linux-lvm-lvm.html)
- [LVM2學習筆記](https://maxubuntu.blogspot.com/2010/05/lvm2.html)
- [技术|如何在 Linux 中创建/配置 LVM逻辑卷管理](https://linux.cn/article-12670-1.html)
- [系统运维|如何在 Linux 中扩展/增加 LVM 大小(逻辑卷调整)](https://linux.cn/article-12673-1.html)
- [系统运维|如何在 Linux 中减少/缩小 LVM 大小(逻辑卷调整)](https://linux.cn/article-12740-1.html)
- [使用 lvm 內建的 raid 1 功能達成硬碟故障時的高可用性](https://gholk.github.io/linux-lvm-raid-1-builtin-boot.html)

292
21.01. Linux/lxc.md Normal file
View File

@@ -0,0 +1,292 @@
這裡分享我在 Ubuntu Server 22.04 用 LXC 安裝 Windows 11以及分享內顯 i915給虛擬機的的紀錄。
要讓虛擬機可以使用 GPU最常見的是 GPU passthrough但是 GPU passthrough 只能給一個虛擬機使用,要讓多個虛擬機同時使用的話,必須打開 i915 的 SR-IOV ,可以多 7 個 VGA來分享給虛擬機。
# 準備
## 安裝 incus
根據 https://linuxcontainers.org/incus/docs/main/installing/#installing 的說明Ubuntu 22.04 還沒辦法使用 apt 來安裝,因此依照 https://github.com/zabbly/incus 的說明來安裝:
```shell
curl -fsSL https://pkgs.zabbly.com/key.asc | gpg --show-keys --fingerprint
mkdir -p /etc/apt/keyrings/
sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
sudo sh -c 'cat <<EOF > /etc/apt/sources.list.d/zabbly-incus-stable.sources
Enabled: yes
Types: deb
URIs: https://pkgs.zabbly.com/incus/stable
Suites: $(. /etc/os-release && echo ${VERSION_CODENAME})
Components: main
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/zabbly.asc
EOF'
sudo apt update
sudo apt install incus
```
## 設定 incus
使用 `sudo incus admin init` 來設定,會問一堆問題,如下:
```
Would you like to use clustering? (yes/no) [default=no]:
Do you want to configure a new storage pool? (yes/no) [default=yes]: no
Would you like to create a new local network bridge? (yes/no) [default=yes]: no
Would you like to use an existing bridge or host interface? (yes/no) [default=no]: yes
Name of the existing bridge or host interface: enp3s0
Would you like the server to be available over the network? (yes/no) [default=no]: yes
Address to bind to (not including port) [default=all]:
Port to bind to [default=8443]:
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]:
Would you like a YAML "init" preseed to be printed? (yes/no) [default=no]:
```
大部分都預設就可以。
## 建立 pool
```shell
sudo incus storage create vmpool dir source=/lvm1/lxd_storage
```
## 建立 profile
```shell
sudo incus profile copy default windows
```
編輯profile內容如下
```
config:
limits.cpu: "16"
limits.memory: 16GiB
raw.qemu: -device intel-hda -device hda-duplex -audio spice
description: 'Windows: 16CPU, 16GB RAM, 1024GB DISK'
devices:
eth0:
name: eth0
nictype: macvlan
parent: br0
type: nic
root:
path: /
pool: vmpool
type: disk
vtpm:
path: /dev/tpm0
type: tpm
name: windows
used_by: []
```
## 安裝需要的套件
```shell
sudo apt-get install genisoimage libguestfs-tools wimtools --yes
sudo snap install distrobuilder --classic
```
## 轉換 ISO
我們需要把Windows ISO轉換為lxc可用的ISO先準備好原本的Windows ISO這裡的檔名是 `Win11_23H2_Chinese_Traditional_x64v2.iso`,記得換成你自己的檔名。
命令如下:
```shell
sudo distrobuilder repack-windows Win11_23H2_Chinese_Traditional_x64v2.iso Win11_23H2_Chinese_Traditional_x64v2.lxd.iso
```
我們把 `Win11_23H2_Chinese_Traditional_x64v2.iso` 轉為 `Win11_23H2_Chinese_Traditional_x64v2.lxd.iso`
等一下 `Win11_23H2_Chinese_Traditional_x64v2.lxd.iso` 就是要餵給虛擬機安裝的檔案。
## 讓操作端跟 server 端可以互連
因為 Ubuntu server 沒有 GUI都是透過命令操作所以當虛擬機開機之後沒有辦法顯示畫面。所以我們需要從另一台 Windows 來把虛擬機開機,並在這台 Windows 上顯示並操作安裝畫面,這樣才可以安裝 Windows 虛擬機。
Ubuntu server 我們就稱為 server 端。
Windows 這邊我們就稱為操作端。
### server 端
這邊我們要讓 lxc 新增一個操作端,如下:
```shell
sudo incus config trust add awn-pc1
```
然後會跳出一堆字串,類似這樣:
```
ucRZ8JO48d4J32MRZ1tuuvNh3Wg86Pb7LZ4QKk090BM3jA2SIRh9aDB1mQ4dZ0H76PJll1bb0YkG2ZK8l5YYPWX5Cw4UhVs2li722P5s7A45MW4B5WBXBQXFPT170L50MsMMjB8ZiVhO6Ug2JWXGwdxp897u8cELqE6AAbA901uUCBwo80qr5czfR2Pk3QS7JCRb9LFa6uKKQsQKSDHm76814UOD3Knf50E9cq3GB6C28A0B54NKBw9T5004ZQ75QqCZqBD0F32mjfAILGCI57R09RN81LHMJ0NLVAXAKQc2LTh0aHYjsO6J41ERLAQF8bk7T07cG4611YT6F57Y6lr93ZSc5L0W7F5R5RG7Xr6e0gJ6dXh5iI18uwpUrZ6MAhN6GSUSGYTqe218He5gLa3G3UMNAJY1PlLSShcV91UF3Z18QloVR8DP9O6wYa7R9LG2Cv04MDSIK8Hs94ZPQ4hsbS1BT2J5QqZ41su06R9a8WTjP9rX8hNsQz5x5744AJl2656SdUqbos4JKX9B2IbGheqK94D7Ya0SZYiODX7Y5rIWdH1P6M6e5J75nN6En0Y9UmgRCGE2WIeG6jXFhpb7
```
這個記下來,等一下要在操作端輸入。
### 操作端
命令:
```
incus remote add awin-pc2 192.168.1.24
```
然後輸入剛剛那一串字串。
接著命令:
```
incus switch awin-pc2
```
之後就可以在操作端啟動 server 端的虛擬機了。
# 建立虛擬機
server 端命令:
```shell
sudo incus init --empty --vm --profile=windows $VM_NAME
sudo incus config device add $VM_NAME install disk source=/home/awin/lxd/Win11_23H2_Chinese_Traditional_x64v2.lxd.iso boot.priority=10
```
操作端命令:
```shell
incus start DENNY-VM-WIN11 --console=vga
```
操作端會跳出一個新視窗,然後就是 Windows 的安裝流程,記得在 "Press any key to boot from CD" 文字出現的時候趕快按一個鍵進入安裝流程。
來不及的話,左上角按鈕可以發送 `ctrl+alt+del` 來重新開機。
這邊要注意的是,一旦發生重開機的狀況,剛剛那個視窗會關掉,然後跳一堆錯誤訊息,像是:
```
(remote-viewer.exe:23816): GSpice-CRITICAL **: 22:48:14.989: _usbdk_hider_update: assertion 'priv->usbdk_api != NULL' failed
(remote-viewer.exe:23816): GLib-GIO-WARNING **: 22:48:15.475: Unexpectedly, UWP app `Microsoft.OutlookForWindows_1.2024.214.400_x64__8wekyb3d8bbwe' (AUMId `Microsoft.OutlookForWindows_8wekyb3d8bbwe!Microsoft.OutlookforWindows') supports 4 extensions but has no verbs
(remote-viewer.exe:23816): GLib-GIO-WARNING **: 22:48:15.598: Unexpectedly, UWP app `Microsoft.ScreenSketch_11.2401.37.0_x64__8wekyb3d8bbwe' (AUMId `Microsoft.ScreenSketch_8wekyb3d8bbwe!App') supports 29 extensions but has no verbs
(remote-viewer.exe:23816): GLib-GIO-WARNING **: 22:48:15.600: Unexpectedly, UWP app `Clipchamp.Clipchamp_3.0.10220.0_neutral__yxz26nhyzhsrt' (AUMId `Clipchamp.Clipchamp_yxz26nhyzhsrt!App') supports 41 extensions but has no verbs
(remote-viewer.exe:23816): GSpice-CRITICAL **: 22:48:15.677: _usbdk_hider_update: assertion 'priv->usbdk_api != NULL' failed
(remote-viewer.exe:23816): GSpice-WARNING **: 22:48:15.677: Warning no automount-inhibiting implementation available
Error: tls: failed to send closeNotify alert (but connection was closed anyway): write tcp 192.168.1.24:8443->192.168.1.154:50714: write: broken pipe
```
這時用 `incus console $VM_NAME --type=vga` 就可以再次連上已開機的虛擬機,不要用 `incus start DENNY-VM-WIN11 --console=vga`
只要虛擬機沒有關機,就是用 `incus console $VM_NAME --type=vga` 來連上畫面。
關機的情況下才是用 `incus start DENNY-VM-WIN11 --console=vga` 來開機並顯示畫面。
再來 Windows 11 沒有網路會無法安裝,所以要跳過網路檢查,在最早的安裝畫面按 `shift+F10` 打開 command line然後輸入
```
oobe/bypassnro
```
安裝之後,把虛擬機連上 macvlan。
```shell
sudo incus config device override $VM_NAME eth0 nictype=macvlan
```
這樣虛擬機就有網路了。
## 虛擬機裝好之後
### 虛擬機操作
1. 把遠端桌面連線打開
2. 下載 driver: [virtio-win-guest-tools.exe](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.248-1/virtio-win-guest-tools.exe) ,然後安裝。
# 分享內顯給虛擬機
## 準備
安裝需要的套件
```shell
apt install build-* dkms
```
確認你的 kernel 是 6.1 ~ 6.5
```shell
uname -r
# 6.5.0-26-generic
```
## 安裝 i915 Driver
下載 i915 Driver
```shell
git clone https://github.com/strongtz/i915-sriov-dkms
```
進到剛剛 clone 的目錄,修改 `dkms.conf`,把 `PACKAGE_NAME` 改為 `"i915-sriov-dkms"`,把 `PACKAGE_VERSION` 改為 `"6.5"`,看你的 kernel 版號多少就改多少。我的版本是 6.5 ,所以這裡填 6.5。
```shell
cd i915-sriov-dkms
nano dkms.conf
# 前 2 行改成這樣
# dkms.conf
PACKAGE_NAME="i915-sriov-dkms"
PACKAGE_VERSION="6.5"
```
安裝 driver
```shell
# 記得要在 i915-sriov-dkms 這目錄裡面
dkms add .
dkms install -m i915-sriov-dkms -v 6.5
dkms remove -m i915-sriov-dkms -v 6.5
dkms install -m i915-sriov-dkms -v 6.5
```
如果有出現類似以下的錯誤:
```
update-initramfs: Generating /boot/initrd.img-6.5.0-26-generic
W: Possible missing firmware /lib/firmware/i915/mtl_gsc_102.0.0.1511.bin for module i915
W: Possible missing firmware /lib/firmware/i915/mtl_huc_8.4.3_gsc.bin for module i915
W: Possible missing firmware /lib/firmware/i915/mtl_guc_70.6.4.bin for module i915
```
去 [https://cgit.freedesktop.org/drm/drm-firmware/refs/](https://cgit.freedesktop.org/drm/drm-firmware/refs/) 把檔案找出來,放到 `/lib/firmware/i915/` 然後再重新安裝。
檢查是否有安裝成功:
```shell
modinfo i915 | grep vf
# 應該要出現如下訊息
# parm: max_vfs:Limit number of virtual functions to allocate. (0 = no VFs [default]; N = allow up to N VFs) (uint)
```
如果沒有就重新安裝試試。
修改 grub
```shell
nano /etc/default/grub
# "GRUB_CMDLINE_LINUX_DEFAULT" 這一行加入:"quiet intel_iommu=on i915.enable_guc=3 i915.max_vfs=7",如下:
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on i915.enable_guc=3 i915.max_vfs=7"
```
更新 grub 和 initramfs
```shell
update-initramfs -u -k all
update-grub
```
開機自動自動設定 vGPU 的數量:
```shell
sudo nano /etc/rc.local
# 加入
echo 7 > /sys/devices/pci0000:00/0000:00:02.0/sriov_numvfs
```
然後 `sudo chmod +x /etc/rc.local`
重開機,開機之後檢查 VGA 數量是不是有 8 個1 個是原本的,另外 7 個是可以分享給虛擬機的 )。
```shell
lspci | grep 00:02
# 應該找到 8 個
00:02.0 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:02.1 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:02.2 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:02.3 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:02.4 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:02.5 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:02.6 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
00:02.7 VGA compatible controller: Intel Corporation AlderLake-S GT1 (rev 0c)
```
## 設定 vGPU 給虛擬機
可以分享的 VGA 有 `00:02.1` ~ `00:02.7` ,然後在虛擬機裡面新增一個 device
```shell
sudo incus config device add $VM_NAME gpu pci address=0000:00:02.1
```
然後在操作端把虛擬機開機:
```
incus start $VM_NAME --console=vga
```
這時後會變成黑畫面,像這樣:
![[incus_srart_display_error.png]]
大概等個 1~3 分鐘就會跳出開機畫面了。
開機好之後,裝置管理員會出現一張顯卡,去 Windows Update 更新,就會出現 Intel UHD 770 顯示卡了。

View File

@@ -0,0 +1,43 @@
# Connect to SMB folder
## 安裝
```shell
sudo apt-get install cifs-utils
```
## 設定
### 建立 `~/.smbcredentials` ,用來存放帳號密碼
```shell
touch ~/.smbcredentials
vim ~/.smbcredentials
```
依下面格式填入
```
username=<your_username>
password=<your_password>
```
### 讓機器每次開機就自動掛載。
建立掛載點
```shell
sudo mkdir /mnt/sambashare
```
`/mnt/sambashare` 改成你自己喜歡的路徑
打開 `/etc/fstab`,填入下面這一行:
```
//samba_server_ip/share_name /mnt/sambashare cifs credentials=/home/pi/.smbcredentials,uid=pi,gid=pi 0 0
```
檢查
```shell
sudo systemctl daemon-reload
sudo mount -a
```
### 範例
```
//192.168.1.11/share /media/share cifs credentials=/home/awin/.smbcredentials_shareview,auto 0 0
//192.168.1.11/upload /media/upload cifs credentials=/home/awin/.smbcredentials_shareview,auto 0 0
//192.168.1.11/Backup /media/backup cifs credentials=/home/awin/.smbcredentials_backup,auto 0 0
```

View File

@@ -0,0 +1,86 @@
# 前置條件
你需要有 **root** 或是 **sudo** 的權限才能更改系統的時區
# 檢查當前時區
在 Ubuntu 中,我們有三種方式查看當前時區設定
## 1. 使用 `timedatectl` 命令顯示當前的系統時區。
```
timedatectl
```
```result
                    Local time: Fri 2021-01-07 22:45:47 UTC
                Universal time: Fri 2021-01-07 22:45:47 UTC
                      RTC time: Fri 2021-01-07 22:45:48
                      Time zone: Etc/UTC (UTC, +0000)
      System clock synchronized: yes
systemd-timesyncd.service active: yes
                RTC in local TZ: no
```
## 2. 查看 `/etc/localtime` 的連結指向
```
ls -l /etc/localtime
```
```result
lrwxrwxrwx 1 root root 33 Jan 8 15:57 /etc/localtime -> ../usr/share/zoneinfo/Etc/UTC
```
## 3. 查看 `/etc/timezome` 的內容
```
cat /etc/timezone
```
```result
Etc/UTC
```
# 修改時區
當我們檢查完時區後,接下來就是修改時區。不過假如你不知道要改的時區名稱,你可以透過以下指令來查看
```
timedatectl list-timezones
```
```result
...
Asia/Seoul
Asia/Shanghai
Asia/Singapore
Asia/Srednekolymsk
Asia/Taipei
Asia/Tashkent
Asia/Tbilisi
Asia/Tehran
Asia/Thimphu
Asia/Tokyo
...
```
找到要修改的時區名稱後,接下就是以 sudo 權限執行以下命令
```
sudo timedatectl set-timezone 要修改的時區名稱
```
例如要將系統修改為台北時區 `Asia/Taipei`,請輸入
```
sudo timedatectl set-timezone Asia/Taipei
```
使用 `timedatectl` 指令確認時區是否正確修改
```
timedatectl
```
```result
                    Local time: Fri 2021-01-08 18:38:49 CST
                Universal time: Fri 2021-01-08 10:38:49 UTC
                      RTC time: Fri 2021-01-08 10:38:49
                      Time zone: Asia/Taipei (CST, +0800)
      System clock synchronized: yes
systemd-timesyncd.service active: yes
                RTC in local TZ: no
```
# 參考
- [如何正確修改 Ubuntu 18.04 的系統時區](https://www.hanktsai.com/2021/01/configure-ubuntu1804-timezone.html)

View File

@@ -0,0 +1,108 @@
## Install
```
sudo apt update && sudo apt install apache2
```
## 測試Apache
```
sudo service apache2 status
```
## 設置虛擬主機Virtual Hosts
假設要建立2個網站*test1.ui-code.com*與*test2.ui-code.com*
### 建立目錄並設置權限Permissions
```
sudo mkdir -p /var/www/test1.ui-code.com/public_html
sudo mkdir -p /var/www/test2.ui-code.com/public_html
sudo chmod -R 755 /var/www
```
### 建立測試頁面
#### 建立test1.ui-code.com的測試頁面
```
sudo nano /var/www/test1.ui-code.com/public_html/index.html
```
填入以下內容:
```html
<html>
<head>
<title>Welcome to test1.ui-code.com</title>
</head>
<body>
<h1>Welcome to test1.ui-code.com</h2>
</body>
</html>
```
#### 建立test2.ui-code.com的測試頁面
```
sudo nano /var/www/test2.ui-code.com/public_html/index.html
```
填入以下內容:
```html
<html>
<head>
<title>Welcome to test2.ui-code.com</title>
</head>
<body>
<h1>Welcome to test2.ui-code.com</h2>
</body>
</html>
```
### 建立虛擬主機文件Virtual Host Files
虛擬主機文件位於 /etc/apache2/sites-available/ 中,其用於告訴 Apache 網頁伺服器如何響應Respond 各種網域請求Request
讓我們為test1.ui-code.com 網域創建一個新的虛擬主機文件。
```
sudo nano /etc/apache2/sites-available/test1.ui-code.com.conf
```
將以下內容貼上:
```
<VirtualHost *:80>
ServerAdmin webmaster@test1.ui-code.com
ServerName test1.ui-code.com
ServerAlias www.test1.ui-code.com
DocumentRoot /var/www/test1.ui-code.com/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```
再來為test2.ui-code.com 網域創建一個新的虛擬主機文件。
```
sudo nano /etc/apache2/sites-available/test2.ui-code.com.conf
```
將以下內容貼上:
```
<VirtualHost *:80>
ServerAdmin webmaster@test2.ui-code.com
ServerName test2.ui-code.com
ServerAlias www.test2.ui-code.com
DocumentRoot /var/www/test2.ui-code.com/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```
### 啟用新的虛擬主機文件Virtual Host Files
現在我們有兩個虛擬主機文件,我們需要使用 a2ensite 工具來啟用它們。
```
sudo a2ensite test1.ui-code.com
sudo a2ensite test2.ui-code.com
```
測試配置語法是否有錯誤。
```
apachectl configtest
```
如果「Syntax OK」重啟 Apache。
```
sudo systemctl reload apache2
```
## 參考
- [[教學][Ubuntu 架站] 在 Ubuntu 20.04 安裝 Apache 網頁伺服器,並架設多個網站(多網域) | 優程式](https://ui-code.com/archives/271)

View File

@@ -0,0 +1,30 @@
## docker-compose.yml
```yaml
version: "3"
networks:
gitea:
external: false
services:
server:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- gitea
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "8020:3000"
- "2244:22"
```
## 文件
- [Gitea Docs: Config Cheat Sheet](https://docs.gitea.io/zh-tw/config-cheat-sheet/)
- [How to Install Gitea on Ubuntu Using Docker](https://www.digitalocean.com/community/tutorials/how-to-install-gitea-on-ubuntu-using-docker)

View File

@@ -0,0 +1,97 @@
# 要準備的檔案
```
├── data
│   ├── grafana
│   │   └── provisioning
│   │   └── datasources
│   │   └── datasources.yaml
│   └── prometheus
│   └── prometheus.yml
├── docker-compose.yml
```
- `docker-compose.yml`
- `data/grafana/provisioning/datasources/datasources.yaml`
- `data/prometheus/prometheus.yml`
# `docker-compose.yml`
```yaml hl:5
services:
grafana:
image: grafana/grafana:latest
restart: always
user: "1000"
ports:
- "8082:3000"
volumes:
- ./data/grafana/data:/var/lib/grafana # data path
- ./data/grafana/grafana.ini:/etc/grafana/grafana.ini
- ./data/grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards
- ./data/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources
environment:
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
prometheus:
image: prom/prometheus:latest
container_name: grafana-prometheus-1
restart: always
command:
- --storage.tsdb.retention.time=7d
- --config.file=/etc/prometheus/prometheus.yml
ports:
- "8083:9090"
volumes:
- ./data/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
node_exporter:
image: prom/node-exporter:latest
restart: always
ports:
- "8084:9100"
```
要注意 `user: "1000"` 這一行,這一行是你的 user ID有可能會變請用 `id -u` 確認一下。
# `datasources.yaml`
```yaml hl:6
# datasources.yaml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://192.168.1.24:8083
access: proxy
```
要注意 `url: http://192.168.1.24:8083` 這一行,要更新 IP 位置。
# `prometheus.yml`
```yaml hl:11
# prometheus.yml
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
scrape_configs:
- job_name: 'node-exporter-local'
scrape_interval: 5s
static_configs:
- targets: ['192.168.1.24:8084']
```
要注意 `- targets: ['192.168.1.24:8084']` 這一行,要更新 IP 位置。
# Grafana template
template ID: `1860`
# 參考
- [Docker Compose 部署监控系统 Prometheus + Grafana + Node Exporter + Cadvisor-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/2016801)
- [基于docker-compose快速搭建Prometheus+Grafana监控系统 - 掘金](https://juejin.cn/post/7320525843737460771)
- [Deep Dive into Using Docker Compose for Monitoring with Prometheus, Grafana, and node_exporter | by mefengl | Medium](https://medium.com/@mefengl/unknown-title-95cb5a15ce83)
- [使用docker-compose快速部署Prometheus+grafana环境_docker-compose安装grafana-CSDN博客](https://blog.csdn.net/weixin_45070882/article/details/132104496)
- [Dashboards | Grafana Labs](https://grafana.com/grafana/dashboards/?collector=nodeexporter&dataSource=prometheus)

View File

@@ -0,0 +1,91 @@
# 設定
`docker-compose.yml` 如下:
```yaml
version: "3"
services:
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: always
user: "1000" # needs to be `id -u` // alternatively chown the grafana/data dir to 472:472
ports:
- "8081:3000" # expose for localhost
links:
- influxdb
volumes:
- ./data/grafana/data:/var/lib/grafana # data path
- ./data/grafana/provisioning:/etc/grafana/provisioning
- ./data/grafana/grafana.ini:/etc/grafana/grafana.ini
environment:
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
influxdb:
image: influxdb
ports:
- "8082:8086"
volumes:
- ./data/influxdb/data:/var/lib/influxdb2
telegraf:
image: telegraf
user: telegraf:992 # Get 992 by `stat -c '%g' /var/run/docker.sock`, depend on system
depends_on:
- influxdb
links:
- influxdb
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
environment:
- HOST_PROC=/proc
- HOST_SYS=/sys
- HOST_ETC=/etc
```
檔案結構如下:
```
data\
grafana\
grafana.ini
telegraf\
telegraf.conf
docker-compose.yml
```
`data/grafana/grafana.ini``data/telegraf/telegraf.conf` 都是需要事先準備好的檔案。
## 設定 InfluxDB
先把 docker 建立起來,然後打開 influxdb[http://awinpi4:8082](http://awinpi4:8082/)),建立帳號、密碼、資料庫名稱。如下:
![[20240217_212138_chrome_setup_influxdb.png]]
之後會出現一串Token如下這個要記起來。
![[20240217_212319_chrome_1894x1254_influxdb_token.png]]
## 設定 telegraf
然後打開 `./data/telegraf/telegraf.conf` ,找到 `[[outputs.influxdb_v2]]` 這個區塊,把 `urls``organization``bucket``token` 的值改為剛剛建立與複製的那一串。如圖:
![[20240217_213900_Code_setup_telegraf_ini.png]]
然後重啟 docker compose。
# 設定 InfluxDB 的 dashboard
到 [https://github.com/influxdata/community-templates#templates](https://github.com/influxdata/community-templates#templates) 挑一個 template例如 [Raspberry Pi System Template](https://github.com/influxdata/community-templates/tree/master/raspberry-pi),找到他的網址,如下:
![[20240217_213108_chrome_1864x1044_raspberrypi_template_on_github.png]]
複製這一行,然後到 InfluxDB 的 template 去把它 import 進來。如下:
![[20240217_213237_chrome_2753x1254_setup_influxdb.png]]
![[20240217_213311_chrome_2753x1254_influxdb_install_template.png]]
接著 Dashboards 就會出現一個 Raspberry Pi System 的 dashboard 了。
![[20240217_213343_chrome_1624x1120_influxdb_dashboard.png]]
點下去之後大概是長這樣:
![[20240217_214001_chrome_2604x1716_influxdb_dashboard.png]]
# 參考
- [建構 Grafana + Influxdb v2.0 + Telegraf 監控系統(docker版) - DSA Learning](https://dsalearning.github.io/grafana/influxdb-telegraf-docker/)
- [Raspberry Pi, InfluxDB, Grafana, Docker | by Anton Karazeev | Medium](https://medium.com/@antonkarazeev/raspberry-pi-influxdb-grafana-docker-a526575d6e6f#id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6ImVkODA2ZjE4NDJiNTg4MDU0YjE4YjY2OWRkMWEwOWE0ZjM2N2FmYzQiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIyMTYyOTYwMzU4MzQtazFrNnFlMDYwczJ0cDJhMmphbTRsamRjbXMwMHN0dGcuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiIyMTYyOTYwMzU4MzQtazFrNnFlMDYwczJ0cDJhMmphbTRsamRjbXMwMHN0dGcuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDM4MDc4MzIzNTMyNTIzMDc3NDYiLCJlbWFpbCI6ImF3aW5odWFuZ0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmJmIjoxNzA4MTQ1MjU3LCJuYW1lIjoiQXdpbiBIdWFuZyIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NJZjNQY0d3WjZIcDdYM204b3BjczRlNGVlZnBHQ1pGeVdMamExcjNTVVNKd1ZnPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkF3aW4iLCJmYW1pbHlfbmFtZSI6Ikh1YW5nIiwibG9jYWxlIjoiemgtVFciLCJpYXQiOjE3MDgxNDU1NTcsImV4cCI6MTcwODE0OTE1NywianRpIjoiNzBmNjI3NDFiNzhiYmVlMTYwNjBiZWRlOTY2YmFjYTAyZTY0ZTZkOSJ9.SXAVZ3SXny4YjIc9Cg6fNFHlXKe0jrm-4uwJ7KH41Tmo_vRAQGlbUn7MmVHXWexpKdpMCSVECC8C1VuUielC-vm8AoHMs1PLJCyhdg02hUyTqEMA08ydscfjguGP6kuI3LoMVsxkAl51C06lQi8llYZ4XGkdHxhCWP12fXQStdGPfv-64KNCkPTIfI7Teo7sfJGyjSQsDMRa4v9GWS9qmbCqut06fhpLyj0lEVfntratbuTN8ThekVfuJyJyG29U6xclm1O0NgBp-BnXML_YtBxTBV2Td_DRYY0dfcVivDKxzH135FfY5xpp_2ZIewkjJG5-pTHpin1R_XLVmIhXuA)
- [Raspberry Pi 4 使用 Grafana 监控_influxdb and grafana on raspberrypi-CSDN博客](https://blog.csdn.net/u013360850/article/details/115568985)

View File

@@ -0,0 +1,143 @@
## docker-compose.yml
```yaml
version: '3'
services:
app:
image: nextcloud
ports:
- 8080:80
volumes:
- ./data:/var/www/html
restart: always
```
## config.php
Nextcloud 的 config 檔放在`/var/www/html/config/config.php`,對應到本機就是 `./data/config/config.php`,在安裝完成之後,需要修改 `trusted_domains``overwriteprotocol``overwrite.cli.url` 這幾個參數,如下:
```php
<?php
$CONFIG = array (
'htaccess.RewriteBase' => '/',
'memcache.local' => '\\OC\\Memcache\\APCu',
'apps_paths' =>
array (
0 =>
array (
'path' => '/var/www/html/apps',
'url' => '/apps',
'writable' => false,
),
1 =>
array (
'path' => '/var/www/html/custom_apps',
'url' => '/custom_apps',
'writable' => true,
),
),
'memcache.distributed' => '\\OC\\Memcache\\Redis',
'memcache.locking' => '\\OC\\Memcache\\Redis',
'redis' =>
array (
'host' => 'redis',
'password' => '',
'port' => 6379,
),
'upgrade.disable-web' => true,
'instanceid' => 'och3g4qo42hq',
'passwordsalt' => 'kASfV5cf4Rh+EcvGNKQkmK01HD2UbI',
'secret' => 'Vze6ZS+qgeOmacEn3ctbtV3uZaEpzeJDufjkkm4A9lgmUYpF',
'trusted_domains' =>
array (
0 => 'nc.awin.one',
),
'datadirectory' => '/var/www/html/data',
'dbtype' => 'mysql',
'version' => '28.0.3.2',
'dbname' => 'nextcloud',
'dbhost' => 'db',
'dbport' => '',
'dbtableprefix' => 'oc_',
'mysql.utf8mb4' => true,
'dbuser' => 'nextcloud',
'dbpassword' => 'F5vy&46Fzbn:hFnlHji.sWfS*SP~wC',
'overwriteprotocol' => 'https',
'overwrite.cli.url' => 'https://nc.awin.one',
'maintenance_window_start' => 1,
'default_phone_region' => 'TWN',
'installed' => true,
'mail_smtpmode' => 'smtp',
'mail_sendmailmode' => 'smtp',
'mail_from_address' => 'awinhuang',
'mail_domain' => 'gmail.com',
'mail_smtphost' => 'smtp.gmail.com',
'mail_smtpport' => '587',
'mail_smtpauth' => 1,
'mail_smtpname' => 'awinhuang@gmail.com',
'mail_smtppassword' => 'stcg ozpc ypsl enbp',
'maintenance' => false,
'loglevel' => 2,
);
```
## 遇到的問題
1. 你經由安全的連線存取系統,但系統卻生成了不安全的 URL。這很有可能是因為你使用了反向代理伺服器但反向代理伺服器的改寫規則並未正常工作請閱讀關於此問題的文件頁面 ↗。
1.`config/config/php` 新增
```
'overwriteprotocol' => 'https',
'overwrite.cli.url' => 'https://nc.awin.one',
```
這兩行
2. 增加 cron: `nextcloud_clean.sh` 在 crontab
1. `*/5 * * * * /home/awin/script/nextcloud_clean.sh`
3. 伺服器未設定維護時段的開始時間。這代表了每天耗費大量資源的背景作業也會在您主要使用的時間內執行。我們建議將其設定為低使用率的時間,以減少使用者受到這些繁重任務帶來的負載影響。
1. 在 `config/config/php` 增加 `'maintenance_window_start' => 1,`
4. 您並未設定手機國際冠碼。設定後使用者在個人檔案設定手機號碼時不必再輸入國際冠碼。若要這樣做請新增「default_phone_region」至設定檔允許的國家及地區請參閱 ISO 3166-1 code 清單。
1. 在 `config/config/php` 增加 `'default_phone_region' => 'TWN',`
5. 您尚未為 WOPI 請求設定允許清單。若無此設定,使用者可以透過 WOPI 請求將受限制的檔案下載至 Nextcloud 伺服器
6. 此站台缺少一些建議的 PHP 模組。為了改善效能與相容性強烈建立您安裝這些模組bz2
7. How to know if redis is being used
```
docker exec -it redis /bin/sh
cd
redis-cli monitor
ctrl+c
```
8. gmail app password for nextcloud: `stcg ozpc ypsl enbp`
9. 大檔案無法上傳
1. 改善傳輸的效率: `sudo docker exec -u 33 e84abbefd5ed php occ config:app:set files max_chunk_size --value 0`
2. `docker-compose.yml` 要增加 3 個 environment
```yaml
- PHP_MEMORY_LIMIT=10240M
- PHP_UPLOAD_LIMIT=10240M
- POST_MAX_SIZE=10240
```
3. ReverseProxy 要增加:
### 大檔案無法上傳
1. 改善傳輸的效率: `sudo docker exec -u 33 e84abbefd5ed php occ config:app:set files max_chunk_size --value 0`
2. `docker-compose.yml` 要增加 3 個 environment
```yaml
- PHP_MEMORY_LIMIT=10240M
- PHP_UPLOAD_LIMIT=10240M
- POST_MAX_SIZE=10240
```
3. ReverseProxy 要增加:
```yaml
client_max_body_size 0;
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
fastcgi_connect_timeout 600;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
```````
4. 上傳檔案時遇到 "server repliedL Not Found": `sudo docker exec -u 33 e84abbefd5ed php occ files:scan --all`

Some files were not shown because too many files have changed in this diff Show More