# Angular+Mqtt+php+Mariadb
## [先參照前篇完成Angular呼叫PHP,MariaDB](https://hackmd.io/@109213067/BJBlg0Mio/https%3A%2F%2Fhackmd.io%2FbGorCTyyT-6MsC1cXhoRIQ%3Fview)
## 情境
* Angular主機發送和顯示訊息(ip 192.168.1.245)
* Mqtt主機接收訊息(ip 192.168.1.244)
## 流程
* 發送訊息: Angular->PHP->Mqtt
* 顯示訊息: Mqtt->Angular->php->Mariadb(存到資料庫)
## 防火牆設定
* Mqtt主機開啟1883和9001 port
`sudo firewall-cmd --permanent --add-port=1883/tcp`
`sudo firewall-cmd --permanent --add-port=9001/tcp`
## 1. 發送訊息
### 安裝php&apache
* [先照前一篇的步驟完成安裝](https://hackmd.io/bGorCTyyT-6MsC1cXhoRIQ?view)
### mqtt設定
* Mqtt主機更改mosquitto設定檔
`$ sudo vim /etc/mosquitto/mosquitto.conf`
```=1
listener 1883 192.168.1.244
```
### php連接mqtt
* [關閉SELinux(可能會阻擋連線)](https://blog.xuite.net/tolarku/blog/195633562-CentOS+%E9%97%9C%E9%96%89+selinux#)
sudo vim /etc/sysconfig/selinux
找到
SELINUX=enforcing
然後修改為
SELINUX=disabled
要重新開機 reboot /restart 後才會套用
* 安裝mqtt-client
`$ composer require php-mqtt/client`
* 在/var/www/html下新增connectMqtt.php
```php=1
<?php
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json; charset=UTF-8');
header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS');
header('Access-Control-Max-Age: 3600');
header('Access-Control-Allow-Headers: Content-Type,Access-Control-Allow-Headers, Authorization, X-Requested-With');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
header('Access-Control-Allow-Credentials: true');
$data = json_decode(file_get_contents("php://input"));
if (!isset($data->message)) {
echo json_encode(['success' => 0, 'message' => 'some fields are empty!']);
exit;
};
$message = $data->message;
require('vendor/autoload.php');
use \PhpMqtt\Client\MqttClient;
use \PhpMqtt\Client\ConnectionSettings;
$server = '192.168.1.244'; // mqtt server ip
$port = 1883;
$clientId = rand(5, 15);
$username = 'emqx_user';
$password = null;
$clean_session = false;
$connectionSettings = new ConnectionSettings();
$connectionSettings
->setUsername($username)
->setPassword(null)
->setKeepAliveInterval(60) // Last Will 設定
->setLastWillTopic('emqx/test/last-will')
->setLastWillMessage('client disconnect')
->setLastWillQualityOfService(1);
$mqtt = new MqttClient($server, $port, $clientId);
$mqtt->connect($connectionSettings);
// 發布 (topic_name, context)
$mqtt->publish('Rocky', $message, 0);
//echo $mqtt;
// 訂閱 (topic_name)
//$mqtt->subscribe('Rocky', function ($topic, $message) {
//printf("Received message on topic [%s]: %s\n", $topic, $message);
//}, 0);
// 持續訂閱
//$mqtt->loop(true);
// 放開連線
$mqtt->disconnect();
?>
```
### angular呼叫php
* data.service.ts新增函式呼叫connectMqtt.php
```c#=1
sendMqtt(text: Object){
return this.http.post('http://localhost/connectMqtt.php',text);
}
```
* app.component.ts新增函式呼叫service的sendMqtt函式
```c#=1
sendMqtt(message: string) {
const text = {message:message};
this.dataService.sendMqtt(text).subscribe(response => {
alert('send text to Mqtt!');
window.location.reload();
});
}
```
* app.component.html新增按鈕呼叫ts的sendMqtt函式
```html=1
<input #sender placeholder="send message"/>
<button (click)="sendMqtt(sender.value);sender.value=''">send</button>
```
### 測試連線
* 一台主機輸入要送出的訊息,另一台主機當作訂閱者

* 送出訊息

### 用app-routing.ts和新增元件把Todo,Mqtt分隔開來
* app-routing.ts內容:
```c#=1
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TodoComponent } from './todo/todo.component'
import { ConnectMqttComponent } from './connect-mqtt/connect-mqtt.component'
const routes: Routes = [
{path:'todo',component:TodoComponent},
{path:'mqtt',component:ConnectMqttComponent},
]
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
```
* app.component.html內容
```html=1
<button routerLink="/todo">Todolist</button>
<button routerLink="/mqtt">Mqtt</button>
<router-outlet></router-outlet>
```
* app.component.ts內容
```c#=1
import { Component, NgModule, OnInit} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
ngOnInit() {
}
title="app";
}
```
* `ng g c todo`新增todo元件
* `ng g c connnect-mqtt`新增connect-mqtt元件
* todo.components.html內容:
```html=1
<h1>Todo List</h1>
<router-outlet></router-outlet>
<h2>Create Job</h2>
<input #myTitle placeholder="job title"/>
<input #myDate placeholder="job date"/>
<input #myContent placeholder="job content"/>
<button (click)="addJob(myTitle.value,myDate.value,myContent.value); myTitle.value='';myDate.value='';myContent.value=''">Create</button>
<table>
<tr><td>num</td><td>title</td><td>date</td><td>content</td><td>done</td></tr>
<tr *ngFor="let i of myJob"><td>{{i.id}}</td><td>{{i.title}}</td><td>{{i.date}}</td><td>{{i.content}}</td><td>{{i.done}}</td><td><button (click)="removeJob(i.id)">Delete</button></td><app-edit-job [theJob]="i"></app-edit-job></tr>
</table>
<input #searcher placeholder="job id"/>
<button (click)="searchJob(searcher.value)">search</button>
<button onClick="window.location.reload()">reset</button><br>
```
* todo.component.ts內容:
```c#=1
import { Component } from '@angular/core';
import { DataService } from '../data.service';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.css']
})
export class TodoComponent {
data:any;
constructor(private dataService: DataService) {}
ngOnInit() {
this.dataService.getData().subscribe((data) => {
this.data = data;
});
}
//
title = 'firstapp';
// defined filter words
//search = "";
// defiend job
//allJobs = [
//{title:"hello",date: new Date("2023-01-01"),content:"say hello",done:false},
//{title:"goodbye",date:new Date("2023-01-02"),content:"say goodbye",done:false},
//]
// display job to html
get myJob() {
//if (this.search=="") {
return this.data;
//return this.allJobs;
//}
//let filterJobs = this.data.filter(function(data:job) {
//return (data.title === filter || data.content === filter);
//});
//return this.data;
}
// function addJob
addJob(title: string, date:string, content: string) {
if (title=="" || date=="Invalid Date" || content=="") {
alert("please fill in all filed!");
return;
}
const job = {title:title,date:date,content:content};
this.dataService.addData(job).subscribe(response=>{
//console.log(response);
alert('add job success!');
window.location.reload();
});
}
searchJob(filter: string) {
const job = {keywords:filter};
this.dataService.searchData(job).subscribe((data) => {
this.data = data;
})
}
// function removeJob
removeJob(id: string) {
const job = {id:id};
this.dataService.deleteData(job).subscribe(response=>{
alert('delete job success!');
window.location.reload();
});
}
convertDate(date: string) {
return new Date(date);
}
}
```
* connect-mqtt.component.html內容:
```html=1
<h1>Mqtt</h1>
<router-outlet></router-outlet>
<input #sender placeholder="send message"/>
<button (click)="sendMqtt(sender.value);sender.value=''">send</button>
```
* connect-mqtt.component.ts內容:
```c#=1
import { Component } from '@angular/core';
import { DataService } from '../data.service';
@Component({
selector: 'app-connect-mqtt',
templateUrl: './connect-mqtt.component.html',
styleUrls: ['./connect-mqtt.component.css']
})
export class ConnectMqttComponent {
constructor(private dataService: DataService) {}
ngOnInit() {
}
//
title = 'Mqtt';
sendMqtt(message: string) {
const text = {message:message};
this.dataService.sendMqtt(text).subscribe(response => {
alert('send text to Mqtt!');
//window.location.reload();
});
}
}
```
## 2. 接收訊息
### 安裝mqtt套件
```
npm install ngx-mqtt
```
### 設定mqtt連線
* app.module.ts內容:
```c#=1
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { EditJobComponent } from './edit-job/edit-job.component';
import { HttpClientModule } from '@angular/common/http';
import { DataService } from './data.service';
import { MqttModule } from 'ngx-mqtt';
import { ConnectMqttComponent } from './connect-mqtt/connect-mqtt.component';
import { TodoComponent } from './todo/todo.component';
@NgModule({
declarations: [
AppComponent,
EditJobComponent,
ConnectMqttComponent,
TodoComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
MqttModule.forRoot({
hostname: '192.168.1.244',
port: 9001,
protocol:'ws',
path: '/mqtt'
}),
],
providers: [DataService],
bootstrap: [AppComponent],
})
export class AppModule { }
```
### 修改mqtt設定檔
`sudo vim /etc/mosquitto/mosquitto.conf`
* 修改內容如下:
```=1
listener 1883 192.168.1.244
protocol mqtt
listener 9001
protocol websockets
```
### php連線Maraidb
* 資料庫新增資料表mqttText,欄位有id(auto_increment)和message
```sql=1
CREATE TABLE `mqttText` (`id` int(10) NOT NULL PRIMARY KEY auto_increment,`messsage` varchar(50) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
* 在/var/www/html新增storeMqtt.php,內容如下:
```php=1
<?php
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json; charset=UTF-8');
header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS');
header('Access-Control-Max-Age: 3600');
header('Access-Control-Allow-Headers: Content-Type,Access-Control-Allow-Headers, Authorization, X-Requested-With');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
header('Access-Control-Allow-Credentials: true');
$servername = "localhost";
$username = "kenny";
$password = "Kenny061256";
$dbname = "test";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
//
$data = json_decode(file_get_contents("php://input"));
//echo json_encode($data->message);
if (!isset($data->content)) {
echo json_encode(['success' => 0, 'message' => 'some fields are empty!']);
exit;
};
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$message = $data->content;
$sql = "insert into `mqttText`(message)values(";
$sql .= "'".$message."')";
//$conn->execute($sql);
$conn->query($sql);
$conn->close();
?>
```
### 修改Angular程式
* data.service.ts內容:
```c#=1
storeMqtt(text:Object){
return this.http.post('http://localhsot/storeMqtt.php',text);
}
```
* connect-mqtt.component.html內容:
```html=1
<h1>Mqtt</h1>
<router-outlet></router-outlet>
<input #sender placeholder="send message"/>
<button (click)="sendMqtt(sender.value);sender.value=''">send</button>
<h2>Message</h2>
<table>
<tr><td>No.</td><td>Text</td></tr>
<tr *ngFor="let text of message;let i = index"><td>{{i+1}}</td><td>{{text}}</td></tr>
</table>
```
* connect-mqtt.component.ts內容:
```c#=1
import { Component } from '@angular/core';
import { DataService } from '../data.service';
import { MqttService } from 'ngx-mqtt';
@Component({
selector: 'app-connect-mqtt',
templateUrl: './connect-mqtt.component.html',
styleUrls: ['./connect-mqtt.component.css']
})
export class ConnectMqttComponent {
message:string[]=[];
constructor(private dataService: DataService,private mqttService: MqttService) {}
ngOnInit() {
this.mqttService.connect();
this.mqttService.observe('Rocky').subscribe((message)=>{
this.message.push(message.payload.toString());
const text = {content:message.payload.toString()};
//console.log(text);
this.dataService.storeMqtt(text).subscribe(response=>{
});
});
}
//
title = 'Mqtt';
sendMqtt(message: string) {
const text = {message:message};
this.dataService.sendMqtt(text).subscribe(response => {
alert('send text to Mqtt!');
//window.location.reload();
});
}
}
```