diff --git a/health-check/adapter/states.go b/health-check/adapter/states.go index 4174dce..f55f134 100644 --- a/health-check/adapter/states.go +++ b/health-check/adapter/states.go @@ -1,14 +1,18 @@ package adapter type States struct { - Timestamp string `json:"timestamp"` - State []NodeState `json:"state"` + Timestamp string `json:"timestamp"` + State HealthInfo `json:"healthinfo"` } type NodeState struct { NodeID int `json:"nid"` State bool `json:"state"` } +type HealthInfo struct { + SinkID int `json:"sid"` + State []NodeState `json:"state"` +} // type HealthInfo struct { // UUID string `json:"n_uuid"` diff --git a/health-check/dataService/memory/statusRepo.go b/health-check/dataService/memory/statusRepo.go index 2c29d43..30320d4 100644 --- a/health-check/dataService/memory/statusRepo.go +++ b/health-check/dataService/memory/statusRepo.go @@ -1,6 +1,7 @@ package memory import ( + "log" "sync" "time" @@ -16,7 +17,7 @@ var ( type statusRepo struct { mu *sync.RWMutex // map[sinkID]map[nodeID] - table map[int]map[int]model.Status + table map[int]map[int]model.Status // 이차원 맵 model.Status가 원소 (0,1,2) } var statusTable *statusRepo @@ -41,7 +42,7 @@ func (sr *statusRepo) Unlock() { sr.mu.Unlock() } -func (sr *statusRepo) UpdateTable(sinkID int, states adapter.States) []model.NodeStatus { +func (sr *statusRepo) UpdateTable(states adapter.States) model.SinkStatus { // ID 번째 싱크를 업데이트 한다. t, err := time.ParseInLocation(timeFmt, states.Timestamp, loc) if err != nil { t = time.Now() @@ -50,18 +51,19 @@ func (sr *statusRepo) UpdateTable(sinkID int, states adapter.States) []model.Nod sr.mu.Lock() defer sr.mu.Unlock() - if _, ok := sr.table[sinkID]; !ok { - sr.table[sinkID] = map[int]model.Status{} + if _, ok := sr.table[states.State.SinkID]; !ok { + sr.table[states.State.SinkID] = map[int]model.Status{} } - return sr.updateNodeStatus(sinkID, states.State, t) + return sr.updateNodeStatus(states.State.SinkID, states.State.State, t) } -func (sr *statusRepo) updateNodeStatus(sinkID int, ns []adapter.NodeState, t time.Time) []model.NodeStatus { +func (sr *statusRepo) updateNodeStatus(sinkID int, ns []adapter.NodeState, t time.Time) model.SinkStatus { // 어답더 계층의 NodeState상태와 메모리 계층의 statusRepo의 status table을 동기화시켜 주는 것 res := []model.NodeStatus{} + rres := model.SinkStatus{} nsTable := map[int]bool{} // update the status checked from the sink node - for _, v := range ns { + for _, v := range ns { // v는 NodeSate 배열 중 한 원소 nsTable[v.NodeID] = true nodeState, ok := sr.table[sinkID][v.NodeID] // if new nodeState, regist new state @@ -79,6 +81,7 @@ func (sr *statusRepo) updateNodeStatus(sinkID int, ns []adapter.NodeState, t tim // if the state is not confirmed from the sink node // check timeout and drop state from table + // sr.table[sinkID][K]랑 nsTable[k]가 존재하지 않을 경우 제거, 존재할 경우 업데이트 for k, v := range sr.table[sinkID] { if _, ok := nsTable[k]; !ok { if v.CheckDrop() { @@ -90,7 +93,11 @@ func (sr *statusRepo) updateNodeStatus(sinkID int, ns []adapter.NodeState, t tim } } - return res + for i, j := range sr.table { + log.Println(i, ":", j) + } + rres = model.SinkStatus{SinkID: sinkID, Satates: res} + return rres } // func (sr *statusRepo) GetKeys() []string { diff --git a/health-check/domain/model/status.go b/health-check/domain/model/status.go index 0fc4587..8262f53 100644 --- a/health-check/domain/model/status.go +++ b/health-check/domain/model/status.go @@ -2,32 +2,36 @@ package model import ( "time" - - "github.com/KumKeeHyun/PDK/health-check/setting" ) const ( - RED = 0 - YELLOW = 1 - GREEN = 2 + RED = 0 // 미동작 + YELLOW = 1 // 형태 바뀔 경우 가운데서 중재 단계 + GREEN = 2 // 동작 ) +type SinkStatus struct { + SinkID int `json:"sid"` + Satates []NodeStatus `json:"states"` +} + + type NodeStatus struct { NodeID int `json:"nid"` State int `json:"state"` } + + type Status struct { State int `json:"state"` Work bool `json:"work"` - Count int `json:"count"` LastConnect time.Time `json:"last_connect"` } -func NewStatus(work bool, t time.Time) Status { +func NewStatus(work bool, t time.Time) Status { // 인자로 받은 work 여부로 Status 구조체 설정 res := Status{ Work: work, - Count: -1, LastConnect: t, } if work { @@ -38,53 +42,40 @@ func NewStatus(work bool, t time.Time) Status { return res } -func (s *Status) setState(v int) { +func (s *Status) setState(v int) { // 인자로 받은 v로 Status구조체 변경 s.State = v switch v { case RED: - s.Count = -1 s.Work = false case GREEN: - s.Count = -1 s.Work = true case YELLOW: - s.Count = setting.StatusSetting.Count s.Work = !s.Work } } -func (s *Status) decreaseCnt() { - if s.Count >= 0 { - s.Count-- - } -} - func (s *Status) UpdateState(work bool, t time.Time) bool { isChange := false // Update time for drop if work { s.LastConnect = t } - if s.Work != work { + if s.State == YELLOW { + if work { + s.setState(GREEN) + } else { + s.setState(RED) + } + isChange = true + } else if s.Work != work { s.setState(YELLOW) isChange = true - } else { - s.decreaseCnt() - if s.Count == 0 { - if s.Work { - s.setState(GREEN) - } else { - s.setState(RED) - } - isChange = true - } } return isChange } - func (s *Status) CheckDrop() bool { s.setState(RED) now := time.Now() - timeout := s.LastConnect.Add(time.Duration(setting.StatusSetting.Drop) * time.Hour) + timeout := time.Now() //s.LastConnect.Add(time.Duration(setting.StatusSetting.Drop) * time.Hour) return now.After(timeout) } diff --git a/health-check/domain/repository/statusRepo.go b/health-check/domain/repository/statusRepo.go index cfd6e7e..40eec6e 100644 --- a/health-check/domain/repository/statusRepo.go +++ b/health-check/domain/repository/statusRepo.go @@ -6,5 +6,5 @@ import ( ) type StatusRepo interface { - UpdateTable(sinkID int, states adapter.States) []model.NodeStatus + UpdateTable(states adapter.States) model.SinkStatus } diff --git a/health-check/go.sum b/health-check/go.sum index d46f6f8..125d8cc 100644 --- a/health-check/go.sum +++ b/health-check/go.sum @@ -1,6 +1,7 @@ github.com/KumKeeHyun/PDK v0.0.0-20200925133928-52ab7f87b199 h1:oatxng65jfuNO8rbSf/Jb7oEy7yCEgvLP+cAy5fnkLU= github.com/KumKeeHyun/PDK/health-check v0.0.0-20200925133928-52ab7f87b199 h1:dLAJIAke48kBX/DkNB77mItO/q6LoDSFTQawG8ParHM= github.com/KumKeeHyun/PDK/health-check v0.0.0-20200925133928-52ab7f87b199/go.mod h1:ztuzK68BTutIOHO2Kefmo9VUGvqtOvc4YqPuREPZzEs= +github.com/KumKeeHyun/toiot v0.0.0-20201116030134-35ec72890c25 h1:DKbvg3iJcV9cG2Xs/Ud68kt0Z8HIji5nG7YSe+nAVHE= github.com/Shopify/sarama v1.27.0 h1:tqo2zmyzPf1+gwTTwhI6W+EXDw4PVSczynpHKFtVAmo= github.com/Shopify/sarama v1.27.0/go.mod h1:aCdj6ymI8uyPEux1JJ9gcaDT6cinjGhNCAhs54taSUo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -78,6 +79,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs= golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= diff --git a/health-check/main.go b/health-check/main.go index 7ad0651..45db4bd 100644 --- a/health-check/main.go +++ b/health-check/main.go @@ -8,7 +8,6 @@ import ( "syscall" "github.com/KumKeeHyun/toiot/health-check/dataService/memory" - "github.com/KumKeeHyun/toiot/health-check/setting" "github.com/KumKeeHyun/toiot/health-check/usecase/healthCheckUC" "github.com/KumKeeHyun/toiot/health-check/usecase/websocketUC" "github.com/gin-gonic/gin" @@ -20,6 +19,7 @@ func main() { event := make(chan interface{}, 10) _ = healthCheckUC.NewHealthCheckUsecase(sr, event) + wu := websocketUC.NewWebsocketUsecase(event) r := gin.New() @@ -41,9 +41,10 @@ func main() { fmt.Println("disconnect websocket!") }) - go log.Fatal(r.Run(setting.Healthsetting.Server)) + go log.Fatal(r.Run(":8085")) sigterm := make(chan os.Signal, 1) signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM) <-sigterm + } diff --git a/health-check/test/tcp test/c2 b/health-check/test/tcp test/c2 new file mode 100755 index 0000000..d074591 Binary files /dev/null and b/health-check/test/tcp test/c2 differ diff --git a/health-check/test/tcp test/client.go b/health-check/test/tcp test/client.go new file mode 100644 index 0000000..5d776a2 --- /dev/null +++ b/health-check/test/tcp test/client.go @@ -0,0 +1,83 @@ +// TCP Client +package main + +import ( + "log" + "net" +) + +func main() { + conn, err := net.Dial("tcp", "10.5.110.11:5032") + if nil != err { + log.Fatalf("failed to connect to server") + } + + // some event happens + conn.Write([]byte(` + { + "sid": 1, + "state": [{ + "nid": 1, + "state": true + },{ + "nid": 2, + "state": false + },{ + "nid": 4, + "state": false + },{ + "nid": 8, + "state": false + },{ + "nid": 27, + "state": false + }] + } + `)) + /* + { + "sid": 1, + "state": [{ + "nid": 1, + "state": false + },{ + "nid": 2, + "state": true + },{ + "nid": 4, + "state": false + },{ + "nid": 8, + "state": true + },{ + "nid": 27, + "state": true + }] + } + { + "sid": 1, + "state": [{ + "nid": 1, + "state": false + },{ + "nid": 2, + "state": true + },{ + "nid": 4, + "state": false + },{ + "nid": 8, + "state": true + },{ + "nid": 27, + "state": true + }] + } + */ + //conn.Write([]byte("005111111111111")) + // for { + // // heartbeat + // conn.Write([]byte("110210101012")) + // time.Sleep(time.Duration(3) * time.Second) + // } +} diff --git a/health-check/usecase/healthCheckUC/healthCheckUsecase.go b/health-check/usecase/healthCheckUC/healthCheckUsecase.go index 27f9af3..28de547 100644 --- a/health-check/usecase/healthCheckUC/healthCheckUsecase.go +++ b/health-check/usecase/healthCheckUC/healthCheckUsecase.go @@ -1,14 +1,14 @@ package healthCheckUC import ( - "fmt" - "sync" + "encoding/json" + "io" + "log" + "net" "time" "github.com/KumKeeHyun/toiot/health-check/adapter" "github.com/KumKeeHyun/toiot/health-check/domain/repository" - "github.com/KumKeeHyun/toiot/health-check/setting" - "github.com/go-resty/resty/v2" ) type healthCheckUsecase struct { @@ -21,39 +21,80 @@ func NewHealthCheckUsecase(sr repository.StatusRepo, e chan interface{}) *health sr: sr, event: e, } + l, err := net.Listen("tcp", "10.5.110.11:8083") // 포트정보 setting으로 옮겨야 함 + if nil != err { + log.Fatalf("fail to bind address to 5032; err: %v", err) + } + //defer l.Close() go func() { - tick := time.Tick(time.Duration(setting.StatusSetting.Tick) * time.Second) for { - select { - case <-tick: - hu.healthCheck() + conn, err := l.Accept() + if nil != err { + log.Printf("fail to accept; err: %v", err) + continue } + go hu.healthCheck(conn) } }() - + /* + go func() { + tick := time.Tick(time.Duration(setting.StatusSetting.Tick) * time.Second) + for { + select { + case <-tick: + hu.healthCheck() + } + } + }() + */ return hu } -func (hu *healthCheckUsecase) healthCheck() { - sinks, err := getSinkList() - if err != nil { - return - } +func (hu *healthCheckUsecase) healthCheck(conn net.Conn) { - var wg sync.WaitGroup - for _, sink := range sinks { - wg.Add(1) - go func(s adapter.Sink) { - res := adapter.States{} - client := resty.New() - client.SetTimeout(500 * time.Millisecond) - resp, _ := client.R().SetResult(&res).Get(fmt.Sprintf("http://%s/health-check", s.Addr)) - - if resp.IsSuccess() { - hu.event <- hu.sr.UpdateTable(s.ID, res) + for { + recvBuf := make([]byte, 4096) + n, err := conn.Read(recvBuf) + if nil != err { + if io.EOF == err { + log.Printf("connection is closed from client; %v", conn.RemoteAddr().String()) + return } - wg.Done() - }(sink) + log.Printf("fail to receive data; err: %v", err) + return + } + if n > 0 { + var healthInfo adapter.HealthInfo + var states adapter.States + + recvBuf = ClearPadding(recvBuf) + log.Println("recv Buf :", recvBuf) + json.Unmarshal(recvBuf, &healthInfo) + + states.State = healthInfo + states.Timestamp = string(time.Now().Unix()) + log.Println("convert to json :", healthInfo) + //test_start + tmphealth := hu.sr.UpdateTable(states) // 변화가 생긴 것들만 뭘로 변했는지 알려줌 ex : {1 [{1 1} {2 1} {8 0}]} + log.Println(tmphealth.Satates) + + hu.event <- tmphealth.Satates + //test_end + + //hu.event <- hu.sr.UpdateTable(sinknum, res) + + } + } +} + +func ClearPadding(buf []byte) []byte { + var res []byte + for i := 1; i < 4096; i++ { + if (buf[i-1] == 125) && (buf[i] == 0) { + res = buf[:i] + break + } } + return res } diff --git a/ui/.env b/ui/.env new file mode 100644 index 0000000..55dfed2 --- /dev/null +++ b/ui/.env @@ -0,0 +1,11 @@ +REACT_APP_DB_IP=10.5.110.11 +REACT_APP_DB_PORT=8081 +REACT_APP_KIBANA_IP=10.5.110.1 +REACT_APP_KIBANA_PORT=5601 +REACT_APP_HEALTHCHECK_IP=10.5.110.11 +REACT_APP_HEALTHCHECK_PORT=8083 +REACT_APP_LOGICCORE_IP=10.5.110.11 +REACT_APP_LOGICCORE_PORT=8081 +REACT_APP_ALARM_IP=0.0.0.0 +REACT_APP_ALARM_PORT=8080 +REACT_APP_KAKAO_MAP_KEY=21ac78abd39b1baa196594d8611e392d \ No newline at end of file diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 8420103..df149d8 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -3,6 +3,7 @@ import { BrowserRouter as Router, Route } from 'react-router-dom'; import Nav from './Navigation'; import SensorManagement from './ManagementComponents/SensorManagement'; import NodeManagement from './ManagementComponents/NodeManagement'; +import ActuatorManagement from './ManagementComponents/ActuatorManagement'; import Dashboard from './KibanaDashboard'; import Visualize from './KibanaVisualize'; import Main from './Home'; @@ -28,6 +29,7 @@ class App extends Component {
+ diff --git a/ui/src/ElemInterface/ElementsInterface.tsx b/ui/src/ElemInterface/ElementsInterface.tsx index eb19680..d5946bc 100644 --- a/ui/src/ElemInterface/ElementsInterface.tsx +++ b/ui/src/ElemInterface/ElementsInterface.tsx @@ -28,6 +28,11 @@ export interface value_list_elem { index: number; } +export interface actuatorListElem { + id: number; + name: string; +} + // sinkList interface export interface sinkListElem { id: number; @@ -86,8 +91,9 @@ export interface topicOptionsElem { // node health check export interface nodeHealthCheckElem { - n_id: number; + nid: number; state: number; + battery: number; } // alarm diff --git a/ui/src/ElemInterface/LcElementsInterface.tsx b/ui/src/ElemInterface/LcElementsInterface.tsx index 8408cdf..75f0e7a 100644 --- a/ui/src/ElemInterface/LcElementsInterface.tsx +++ b/ui/src/ElemInterface/LcElementsInterface.tsx @@ -10,9 +10,14 @@ export interface timeRange { end: string; } +export interface control { + value: number; + sleep: number; +} + export interface logicElem { elem: string; - arg: lcValueArg | lcTimeArg | lcGroupArg | lcActionArg; + arg: lcValueArg | lcTimeArg | lcGroupArg | lcActionArg | lcActuator; } export interface lcValueArg { @@ -32,6 +37,11 @@ export interface lcActionArg { text: string; } +export interface lcActuator { + aid: number; + motion: Array; +} + export interface logicListElem { id: string; // request: undefined, receive: number logic_name: string; diff --git a/ui/src/LogicCoreComponents/InputCards/InputActionCard.tsx b/ui/src/LogicCoreComponents/InputCards/InputActionCard.tsx index 3be0e49..5e01743 100644 --- a/ui/src/LogicCoreComponents/InputCards/InputActionCard.tsx +++ b/ui/src/LogicCoreComponents/InputCards/InputActionCard.tsx @@ -1,6 +1,8 @@ +import { type } from 'jquery'; import React, { Component } from 'react'; import Select from 'react-select'; -import { logicElem } from '../../ElemInterface/LcElementsInterface'; +import { control, logicElem } from '../../ElemInterface/LcElementsInterface'; +import InputActuatorCard from './InputActuatorCard'; import '../LogicCore.css'; interface InputActionCardProps { @@ -11,10 +13,11 @@ interface InputActionCardProps { interface InputActionCardState { elem: string; - arg: { + arg: { text: string; }; } + interface actionOptionsElem { label: string; value: string; @@ -31,14 +34,14 @@ class InputActionCard extends Component< state: InputActionCardState = { elem: '', arg: { text: '' }, - }; + } // Handle action change (select alarm or email) handleActionChange = async (e: any) => { - // Change this state and then.. await this.setState({ elem: e.value, }); + console.log(this.state.elem + '!!!!!@@#@####!@#!'); // change parent's state this.props.handleInputActionCardChange(this.state); }; @@ -46,7 +49,9 @@ class InputActionCard extends Component< // Handle text change by typing handleTextChange = async (e: React.ChangeEvent) => { await this.setState({ - arg: { text: e.target.value }, + arg: { + text: e.target.value, + }, }); this.props.handleInputActionCardChange(this.state); }; @@ -55,7 +60,9 @@ class InputActionCard extends Component< let actionOptions: Array = [ { label: 'alarm', value: 'alarm' }, { label: 'email', value: 'email' }, + { label: 'actuator', value: 'actuator'}, ]; + return (
@@ -94,9 +101,9 @@ class InputActionCard extends Component<
-
+ {/*
*/} {this.state.elem === 'alarm' ? ( // If user select alarm -
+
Alarm MSG -
+
) : this.state.elem === 'email' ? ( // If user select email -
+
Email address
+ ) : this.state.elem === 'actuator' ? ( + ) : (
)}
-
+ //
); } } diff --git a/ui/src/LogicCoreComponents/InputCards/InputActuatorCard.tsx b/ui/src/LogicCoreComponents/InputCards/InputActuatorCard.tsx new file mode 100644 index 0000000..0e84750 --- /dev/null +++ b/ui/src/LogicCoreComponents/InputCards/InputActuatorCard.tsx @@ -0,0 +1,193 @@ +import { type } from 'jquery'; +import React, { Component } from 'react'; +import Select from 'react-select'; +import { control, logicElem } from '../../ElemInterface/LcElementsInterface'; +import '../LogicCore.css'; + +interface InputActionCardProps { + handleInputActionCardChange: (value: logicElem) => void; + //handleRemoveInputActionCardClick: () => void; + //index: number; +} + +interface InputActionCardState { + elem: string; + arg: { + aid: number; + motion: Array; + }; +} +interface actionOptionsElem { + label: string; + value: string; +} + +/* +InputActionCard +- Get input of action element +*/ +class InputActionCard extends Component< + InputActionCardProps, + InputActionCardState +> { + state: InputActionCardState = { + elem: 'actuator', + arg: { + aid: 0, + motion:[{ value: 0, sleep: 0 }] + }, + }; + + // Handle action change (select alarm or email) + handleActionChange = async (e: any) => { + // Change this state and then.. + if (e.value === 'motor') { + await this.setState({ + arg: { + aid: 1, + motion: this.state.arg.motion, + } + }); + console.log(this.state.arg.aid); + } + else if (e.value === 'switch') { + await this.setState({ + arg: { + aid: 2, + motion: this.state.arg.motion, + } + }); + } + this.props.handleInputActionCardChange(this.state); + }; + + // Handle text change by typing + handleTextChange = async (e: React.ChangeEvent) => { + await this.setState({ + arg: { + aid: this.state.arg.aid, + motion: this.state.arg.motion, + }, + }); + this.props.handleInputActionCardChange(this.state); + }; + + handleControlChange = (idx: number) => async (e:any) => { + const new_motion_elem = this.state.arg.motion.map( + (motionElem: control, sidx: number) => { + if (idx !== sidx) return motionElem; + if (e.target.id === 'actuator_value') + return { ...motionElem, value: parseInt(e.target.value) }; + return { ...motionElem, sleep: parseInt(e.target.value) }; + + } + ); + + await this.setState({ + arg: { + aid: this.state.arg.aid, + motion: new_motion_elem + } + }); + + this.props.handleInputActionCardChange(this.state); + }; + + handleAddClick = async () => { + await this.setState({ + arg: { + aid: this.state.arg.aid, + motion: [...this.state.arg.motion, {value: 0, sleep: 0}], + }, + }); + this.props.handleInputActionCardChange(this.state); + }; + + handleRemoveClick = (idx: number) => async () => { + await this.setState({ + arg: { + aid: this.state.arg.aid, + motion: this.state.arg.motion.filter( + (s: any, sidx: number) => idx !== sidx + ), + }, + }); + this.props.handleInputActionCardChange(this.state) + }; + + render() { + let actuatorOptios: Array = [ + { label: 'motor', value: 'motor'}, + { label: 'switch', value: 'switch'}, + ] + return ( +
+
+ +
+ sleep + +< button + className="btn btn-sm" + type="button" + id="button-addon2" + onClick={this.handleRemoveClick(idx)} + > + + + + +
+ ))} +
+
+ ); + } +} + +export default InputActionCard; diff --git a/ui/src/LogicCoreComponents/RegisterLogic.tsx b/ui/src/LogicCoreComponents/RegisterLogic.tsx index 774737b..7935cd2 100644 --- a/ui/src/LogicCoreComponents/RegisterLogic.tsx +++ b/ui/src/LogicCoreComponents/RegisterLogic.tsx @@ -146,6 +146,7 @@ class RegisterLogic extends Component<{}, RegisterLogicState> { handleActionCardChange = (idx: number) => (selectedAction: logicElem) => { // Action card is updated dynamic. It can be added or removed freely. // so find changing field by using received idx and change state. + console.log(selectedAction.elem + "!!!!"); const new_selected_action = this.state.selected_action.map( (action: logicElem, sidx: number) => { if (idx !== sidx) return action; @@ -237,7 +238,9 @@ class RegisterLogic extends Component<{}, RegisterLogicState> { this.state.selected_action ); - // Filter elem: 'empty' field + console.log(this.state.selected_action[0] + "!@!@!@!!@@!@"); + + // Filter elem: 'empty' fie ld elems = elems.filter(function (logic) { return logic.elem !== 'empty'; }); diff --git a/ui/src/LogicCoreComponents/ShowCards/ShowActionCard.tsx b/ui/src/LogicCoreComponents/ShowCards/ShowActionCard.tsx index 1286e88..ef771b4 100644 --- a/ui/src/LogicCoreComponents/ShowCards/ShowActionCard.tsx +++ b/ui/src/LogicCoreComponents/ShowCards/ShowActionCard.tsx @@ -3,6 +3,8 @@ import '../LogicCore.css'; import { logicElem, lcActionArg, + lcActuator, + control, } from '../../ElemInterface/LcElementsInterface'; interface ShowActionCardProps { @@ -16,6 +18,10 @@ ShowActionCard class ShowActionCard extends Component { render() { var action = this.props.logic_elem.arg as lcActionArg; + var control = (this.props.logic_elem.arg as lcActuator).motion; + var motion_name = (this.props.logic_elem.arg as lcActuator).aid; + if (motion_name === 1) var name = 'motor'; + else name = 'switch'; return (
@@ -41,6 +47,20 @@ class ShowActionCard extends Component { : {action.text}
+ ) : this.props.logic_elem.elem === 'actuator' ? ( +
+ {control.map((control: control, idx: number) => ( +
+ + [actuator #{idx}] + +
+ + motion: {name} / value: {control.value} / sleep: {control.sleep} + +
+ ))} +
) : (
)} diff --git a/ui/src/LogicCoreComponents/ShowCards/ShowValueCard.tsx b/ui/src/LogicCoreComponents/ShowCards/ShowValueCard.tsx index e846373..1fac4e9 100644 --- a/ui/src/LogicCoreComponents/ShowCards/ShowValueCard.tsx +++ b/ui/src/LogicCoreComponents/ShowCards/ShowValueCard.tsx @@ -33,7 +33,7 @@ class ShowValueCard extends Component { {range.map((range: numRange, idx: number) => (
- range #{idx} + [range #{idx}]
diff --git a/ui/src/LogicCoreComponents/ShowLogic.tsx b/ui/src/LogicCoreComponents/ShowLogic.tsx index 6161d4b..ffd3c50 100644 --- a/ui/src/LogicCoreComponents/ShowLogic.tsx +++ b/ui/src/LogicCoreComponents/ShowLogic.tsx @@ -76,7 +76,7 @@ class ShowLogic extends Component { ))} {this.props.logic.elems .filter(function (element) { - return element.elem === 'alarm' || element.elem === 'email'; + return element.elem === 'alarm' || element.elem === 'email' || element.elem === 'actuator'; }) .map((actionCard: logicElem, idx: number) => ( diff --git a/ui/src/ManagementComponents/ActuatorManagement.tsx b/ui/src/ManagementComponents/ActuatorManagement.tsx new file mode 100644 index 0000000..a642a90 --- /dev/null +++ b/ui/src/ManagementComponents/ActuatorManagement.tsx @@ -0,0 +1,34 @@ +import React, { Component } from 'react'; +import RegisterActuator from './Register/RegisterActuator'; +import ActuatorTable from './Table/ActuatorTable'; + +/* +SensorManagement +- Manage sensor table, register sensor +*/ +class ActuatorManagement extends Component { + render() { + return ( + <> +
+ + +
+
+

Actuator

+ +
+ + ); + } +} + +export default ActuatorManagement; diff --git a/ui/src/ManagementComponents/Register/RegisterActuator.tsx b/ui/src/ManagementComponents/Register/RegisterActuator.tsx new file mode 100644 index 0000000..a02759e --- /dev/null +++ b/ui/src/ManagementComponents/Register/RegisterActuator.tsx @@ -0,0 +1,124 @@ +import React, { Component } from 'react'; +import { ACTUATOR_URL } from '../../defineUrl'; + +interface RegisterActuatorState { + name: string; + nameValid: boolean; +} + +class RegisterActuator extends Component<{}, RegisterActuatorState> { + state: RegisterActuatorState = { + name: '', + nameValid: false, + }; + + handleNameChange = (e: React.ChangeEvent) => { + if (e.target.value.length > 0) { + this.setState({ + name: e.target.value, + nameValid: true, + }); + } else { + this.setState({ + name: e.target.value, + nameValid: false, + }); + } + console.log(this.state.name + '@@2!!@!@'); + }; + + handleSubmit = (e: React.MouseEvent) => { + e.preventDefault(); + + var url = ACTUATOR_URL; + var data = this.state; + + if (!this.state.nameValid) { + alert('Please enter actuator name.'); + return; + } + + var submitValid: boolean; + submitValid = window.confirm('Are you sure to register this actuator?'); + if (!submitValid) { + return; + } + alert('data : '+data+', url : ' + url); + fetch(url, { + method: 'POST', // or 'PUT' + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json', + }, + }) + .then((res) => res.json()) + .then((response) => console.log('Success:', JSON.stringify(response))) + .catch((error) => console.error('Error:', error)) + .then(() => window.location.reload(false)); + }; + + render() { + return ( + <> + + + ); + } +} + +export default RegisterActuator; \ No newline at end of file diff --git a/ui/src/ManagementComponents/Table/ActuatorTable.tsx b/ui/src/ManagementComponents/Table/ActuatorTable.tsx new file mode 100644 index 0000000..8d7b10d --- /dev/null +++ b/ui/src/ManagementComponents/Table/ActuatorTable.tsx @@ -0,0 +1,119 @@ +import React, { Component } from 'react'; +import { ACTUATOR_URL } from '../../defineUrl'; +import { actuatorListElem } from '../../ElemInterface/ElementsInterface'; +import Pagination from '../Pagination'; + +//import DeleteRequest from './DeleteRequest' + +interface ActuatorTableState { + actuatorList: Array + currentPage: number; + pages: number; +} + +/* +SensorTable +- Show up sensor list. +*/ +class ActuatorTable extends Component<{}, ActuatorTableState> { + state: ActuatorTableState = { + actuatorList: [], + currentPage: 1, + pages: 0, + }; + + componentDidMount() { + this.getsensorList(this.state.currentPage); + } + + // Get sensor list from backend per page + getsensorList(page: number) { + var url = ACTUATOR_URL + '?page=' + page; + + fetch(url) + .then((res) => res.json()) + .then((data) => { + page === 1 + ? this.setState({ actuatorList: data.actuators, pages: data.pages }) + : this.setState({ actuatorList: data.actuators }); + }) + .catch((error) => console.error('Error:', error)); + } + + // Handle click event of the Remove button + handleRemoveClick = (actuator_id: number) => () => { + var url = ACTUATOR_URL + '/' + actuator_id; + + fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }) + .then((res) => res.json()) + .catch((error) => console.error('Error:', error)) + .then(() => window.location.reload(false)); + }; + + handlePageChange = (page: number) => { + this.setState({ currentPage: page }); + this.getsensorList(page); + }; + + render() { + return ( + <> + + + + + + + + + + + {this.state.actuatorList.map( + (actuator: actuatorListElem, idx: number) => ( + + + + + + + ) + )} + +
#nameid
{idx + 10 * (this.state.currentPage - 1)}{actuator.name}{actuator.id} + +
+ + + ); + } +} + +export default ActuatorTable; diff --git a/ui/src/ManagementComponents/Table/MapNodeTable.tsx b/ui/src/ManagementComponents/Table/MapNodeTable.tsx index a5e967b..e56e51b 100644 --- a/ui/src/ManagementComponents/Table/MapNodeTable.tsx +++ b/ui/src/ManagementComponents/Table/MapNodeTable.tsx @@ -39,7 +39,7 @@ class MapNodeTable extends Component { // Find node state(health) and represent as colors (red - yellow - green, gray) findNodeState = (id: number) => { for (let prop in this.props.nodeState) { - if (this.props.nodeState[prop].n_id === id) { + if (this.props.nodeState[prop].nid === id) { return ( { return ●; }; + findNodeBattery = (id: number) => { + for (let prop in this.props.nodeState) { + if (this.props.nodeState[prop].nid === id) { + var battery = this.props.nodeState[prop].battery; + if ( battery === 0 ) + return External power + if ( battery === 255 ) + return Not measurable + // return {battery} + } + } + return 200 + }; + render() { return ( <> @@ -65,6 +79,7 @@ class MapNodeTable extends Component { id sensors health + battery @@ -76,6 +91,7 @@ class MapNodeTable extends Component { {node.id} {node.sensors.map((sensor: any) => sensor.name + ', ')} {this.findNodeState(node.id)} + {this.findNodeBattery(node.id)}
  • diff --git a/ui/src/defineUrl.tsx b/ui/src/defineUrl.tsx index 155d824..0dadae9 100644 --- a/ui/src/defineUrl.tsx +++ b/ui/src/defineUrl.tsx @@ -50,6 +50,10 @@ export const SINK_URL = 'http://' .concat(':') .concat(process.env.REACT_APP_DB_PORT) .concat('/regist/sink'); +export const ACTUATOR_URL = 'http://' + .concat(process.env.REACT_APP_DB_IP) + .concat(':') + .concat('/regist/actuator'); export const TOPIC_URL = 'http://' .concat(process.env.REACT_APP_DB_IP) .concat(':') @@ -57,9 +61,9 @@ export const TOPIC_URL = 'http://' .concat('/regist/topic'); export const LOGICCORE_URL = 'http://' - .concat(process.env.REACT_APP_LOGICCORE_IP) + .concat(process.env.REACT_APP_DB_IP) .concat(':') - .concat(process.env.REACT_APP_LOGICCORE_PORT) + .concat(process.env.REACT_APP_DB_PORT) .concat('/regist/logic'); export const HEALTHCHECK_URL = 'ws://'