كيف صنعت حوض سمك ذكي (الواجهة الأمامية)

مقدمة





كما ذكرت هنا ، بدأت في بناء حوض مائي ذكي يعتمد على السبورة NodeMCU. على ذلك ، استخدمت البرنامج الثابت من micropython، وقمت بإعداد خادم ويب وقمت بإنشاء واجهة برمجة تطبيقات لمعالجة جميع الأجهزة الطرفية وأجهزة الاستشعار. نظرًا لأنه تم التخطيط أصلاً لإصدار حوض السمك الذكي الخاص بي كإصدار قائم بذاته ، فقد أردت إنشاء واحد UIلتتبع جميع العمليات ولإجراء تعديلات يدوية. في كل مرة ، استخدم طرقًا من النوع: http://192.168.1.70/led_controller?impulse=4000&level=200&ledName=whiteكانت كئيبة للغاية وغير مريحة. خاصةً عندما تكون قد ذهبت بالفعل إلى السرير وكان هاتفك فقط في متناول اليد. ومرة أخرى ، كنت أرغب في الحصول على مستوى أعلى في التطوير والقيام بشيء ممتع.



UI Vue.js. , .. " " WI-FI . , . , , . , , SPA(" ": "single page application"), , . backend — LED- . , vue-cli:



$ vue ui
  Starting GUI...
  Ready on http://localhost:8000




, :



  • vue-bootstrap — .
  • axiosbackend API.
  • vuex


axios url



plugin/axios.js



import Vue from 'vue';
import axios from 'axios';
import VueAxios from 'vue-axios';

axios.defaults.baseURL = 'http://192.168.1.70';

Vue.use(VueAxios, axios);


— , , , . .





App.vue



<template>
  <div id="app">
    <b-navbar type="dark" variant="primary" class="rounded">
      <b-navbar-brand tag="h1" class="mb-0">Fish Tank</b-navbar-brand>
      <b-icon 
        icon="brightness-alt-high" 
        font-scale="3" 
        variant="light" 
        class="rounded bg-primary p-1"
      />
    </b-navbar>
    <list-of-range-controllers/>
  </div>
</template>

<script>
import ListOfRangeControllers from './components/ListOfRangeControllers';

export default {
    name: 'App',
    components: {
        ListOfRangeControllers
    }
}
</script>

<style scoped>
  #app {
      margin: 50px 20px;
  }
</style>


ثم فكرت في كيفية تنظيم منطق الأعمال نفسه وفصله عن النموذج. قررت أن Vuexأجربها بالكامل من خلال Vyuks نفسها ولم تنقسم ، لكنها فعلت كل شيء في ملف واحد. بالنسبة لمستوى LED ، أستخدم مقياسًا من 0 - 100 %، بينما backendيتم تعيين مستوى الضوء نفسه من 0 - 1024الوحدات. بعد التقريب ، اعتقدت أنني سأضرب ببساطة في 10 عندما يتم إرسال البيانات عن طريقPOST طلب أو أقسمها على 10 عندما يتم تلقي البيانات عن طريقGET طلب.



store/index.js



import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        whiteLED         : 0,
        waterTemperature : 0,
    },

    mutations: {
        'SYNC_WHITE_LED' (state, level) {
            state.whiteLED = level;
        },
        'SYNC_WATER_TEMPERATURE' (state, level) {
            state.waterTemperature = level;
        },
        'SET_WHITE_LED' (state, level) {
            state.whiteLED = level;
        },
        'SET_HEATER_LEVEL' (state, level) {
            state.waterTemperature = level;
        }
    },

    actions: {
        async syncWhiteLED({commit}) {
            try {
                const response = await Vue.axios.get('/get_led_info?ledName=white');
                commit('SYNC_WHITE_LED', response.data['level']/10);
            }
            catch(error) {
                console.error(error);
            }
        },
        async syncWaterTemperature({commit}) {
            try {
                const response = await Vue.axios.get('/get_water_tmp');
                commit('SYNC_WATER_TEMPERATURE', response.data['water_temperature_c']);
            }
            catch(error) {
                console.error(error);
            }
        },
        async setWhiteLED({commit}, level) {
            try {
                await Vue.axios.get(`/led_controller?impulse=4000&level=${level*10}&ledName=white`);
                commit('SET_WHITE_LED', level);
            }
            catch(error) {
                console.error(error);
            }
        },
        async setWaterTemperature({commit}, level) {
            try {
                await Vue.axios.get(`/heater_control?params=${level}`);
                commit('SET_HEATER_LEVEL', level);
            }
            catch(error) {
                console.error(error);
            }
        },
    },

    getters: {
        whiteLED: state => {
            return state.whiteLED;
        },
        waterTemperature: state => {
          return state.waterTemperature;
        },
    }
})




بعد ذلك ، أقوم بعمل مكون عالمي حيث سيتم عرض القيمة الحالية ، ومقياس لتغيير القيمة وزرين للمزامنة والتغيير الفعلي.

components/ui/RangeController.vue



<template>
    <b-card 
        :title="header" 
    >
        <b-alert show>
            Change to : {{ controllerValue }} 
                        {{
                            name.match(/Water/gi) 
                            ? 'C\u00B0' : '%'
                        }}
        </b-alert>
        <b-form-input 
            type="range"
            :min="min"
            :max="max"
            v-model="controllerValue"
        />
        <b-button 
            variant="outline-primary" 
            size="sm"
            @click="$emit(`${buttonChangeName}Change`, controllerValue)"
        >
            {{ changeButton }}
        </b-button>
        <b-button 
            class="float-right"
            variant="outline-success" 
            size="sm"
            @click="$emit(`${buttonChangeName}Sync`)"
        >
            Sync value
        </b-button>
    </b-card>
</template>

<script>
export default {
    props: {
        name: {
            type    : String,
            default : 'Header',
        },
        value: {
            type    : Number,
            default : 0,
        },
        buttonChangeName: {
            type    : String,
            default : 'Change'
        },
        min: {
            type    : Number,
            default : 0
        },
        max: {
            type    : Number,
            default : 100
        }
    },
    data() {
        return {
            controllerValue: this.min,
        }
    },
    computed: {
        header() {
            const isWater = this.name.match(/Water/gi);
            const postfix = isWater ? 'C\u00B0' : '%';

            const sufix = isWater ? 'Temperature' : this.name.match(/Pump/gi)? '' : 'LED';

            return `${this.name} ${sufix} is : ${this.value} ${postfix}`;
        },
        changeButton() {
            return `${this.buttonChangeName} change`;
        },
    }
}
</script>


, , , - DRY, , , .



components/ListOfRangeControllers.vue



<template>
    <b-container class="bv-example-row mt-4 mb-4">
      <h1>Backlight</h1>
      <b-row>
        <b-col v-for="led in leds" :key="led.name">
          <range-controller
            :name="led.name"
            :value="led.value"
            :buttonChangeName="led.buttonName"
            v-on="{ 
              ledWhiteChange : ledWhiteChange,
              ledWhiteSync   : ledWhiteSync,
            }"
          />
        </b-col>

      </b-row>
      <h1>Temperature</h1>
      <b-row>
        <b-col>
          <range-controller
              name="Water"
              :value="waterTemperature"
              :min="20"
              :max="45"
              buttonChangeName="temperature"
              @temperatureChange="temperatureChange"
              @temperatureSync="temperatureSync"
          />
        </b-col>
      </b-row>

    </b-container>
</template>

<script>
import RangeController from './ui/RangeController';
import { mapActions, mapGetters } from 'vuex'

export default {
    components: {
        RangeController
    },

    methods: {
        ...mapActions([
            'syncWhiteLED',
            'syncWaterTemperature',
            'setWhiteLED',
        ]),

        ledWhiteChange(value) {
            this.setWhiteLED(value);
        },

        //  
        temperatureChange(value) {
            console.log('temp is changed!' + `${value}`);
        },

        ledWhiteSync() {
            this.syncWhiteLED();
        },
        async temperatureSync() {
            await this.syncWaterTemperature();

            console.log(this.waterTemperature);
        },
    },

    computed: {
        ...mapGetters([
            'waterTemperature',
            'whiteLED',
        ]),

        leds() {
            return [
                {
                    name: 'White',
                    value: this.$store.getters.whiteLED,
                    buttonName: 'ledWhite',
                },
            ]
        },
    },
}
</script>










UI , , . , . Vue , … , , , . NodeMCU Vue . . , , . , . . , , :



  • - (Ph)
  • (kH)


- , , . ? . — . NodeMCU "", .






All Articles