web 语音通话 jssip

news/2024/7/19 12:53:25 标签: 前端, js, vue, webrtc

先把封装好的地址安上(非本人封装):webrtc-webphone: 基于JsSIP开发的webrtc软电话

jssip中文文档:jssip中文开发文档(完整版) - 简书

jssip使用文档:(我没有运行过,但是他写的很清楚,反正比我好)jssip+webrtc+freeswitch实现电话网页及遇到的488状态码问题_freeswitch 488_weixin_39715323的博客-CSDN博客

正常使用由于web限制应该在https上使用,但是http也不是不可以,我放到下一篇文章了,这就说怎么使用

我这用的是webrtc+jssip

webrtc-webphone已经实现了我的需求,所以我没有使用原生jssip

特主要实现的功能有:注册,拨叫,接听,保持,恢复

我的项目中需要静音(指我不能说话,通话人可以讲话)

所以我将hold(保持)和unhold(恢复)改成了mute和unmute

 下面是完整代码和方法说明:

1.init(注册citbar)config中需要使用ip地址、端口号、extNo拨号人、extPwd密码

2.handleAgentBarBtnClick(通话状态更改)

        makecall(拨号)

        hangup(挂断)

        hold(静音)

        unhold(取消静音)

3.onbeforeunload (通话中刷新对讲群组中没有退出,导致群组中有多个同一个人)

4.beforeDestroy(切换页面后没有退出群组,刷新不走这个方法)

注意:

1.不要重复拨号,状态卡住后就能同一个设备对话了bug

2.如果想切换页面还能通话就不要beforeDestroy,但是在其他页面在回来时会导致状态不一致,而且容易出现卡状态,所以我将通话操作放在index最顶层里面

js"><template>
  <div class="top">
   
    <div style="padding: 0 30px;">
      <div style="display: flex;padding-top: 3px;">

        <!-- 设备树插件 -->
        <organizationTree ref="organizationTree"></organizationTree>

        <div style="width:77vw;height:80vh; margin-top: 5px;">
          <div class="photo_date" style="position:relative">
            <div style="display:flex;min-width:30%">
              <div class="text_type photo_deviceName " v-show="groupName" style="text-align: center;">
                {{groupName}}</div>
              <div class="text_type" :class="meetingStatus==2?' meetingName1':' meetingName'"
                style="width: 125px;text-align: center;">
                {{meetingStatus==2?meetingName:meetingName1}}</div>
            </div>
            <div style="position: absolute;right: 0;" v-show="createBy==userName">

              <div v-show="meetingStatus==2" class="btn_type photo_meeting_btn" @click="meetingAll"
                style="position:absolute; right:135px;width: 140px;height: 35px;line-height: 35px;">
                邀请全部成员</div>
              <div class="bg_btn_type photo_meeting_btn" @click="meeting"
                style="position:absolute; right:0;width: 125px;height: 35px;line-height: 35px;">
                {{meetingStatus==1?'开始会议':'结束会议'}}</div>

            </div>
          </div>

          <div class="">
            <div class="deviceList_title">
              <div class="deviceList_title_text" style="width:15%">名称</div>
              <div class="deviceList_title_text" style="width:15%">imei</div>
              <div class="deviceList_title_text" style="width:15%">类型</div>
              <div class="deviceList_title_text" style="width:15%">状态</div>
              <div class="deviceList_title_text" style="width:15%">会议状态</div>
              <div class="deviceList_title_text" style="width:24%">操作</div>
              <!-- <div class="deviceList_title_text">操作</div> -->
            </div>
            <div v-show="deviceList" class="deviceList">

              <div class="deviceList_list" :class="item.id==itemId?'deviceList_list1':''"
                v-for="(item,index) in deviceList" :key="item.id" @mouseover="mouseover(item.id)"
                @mouseleave="mouseout()">
                <!-- <el-tooltip :content="item.name" placement="bottom" effect="light"> -->
                <div class="deviceList_title_text" style="width:15%">{{item.devName}}</div>
                <!-- </el-tooltip> -->
                <div class="deviceList_title_text" style="width:15%">{{item.imei}}</div>
                <div class="deviceList_title_text" style="width:15%">{{item.devTypeName}}
                </div>
                <div class="deviceList_title_text" style="width:15%">
                  {{dictionary(item.devStatus,'dev_status',item.imei)}}
                </div>
                <div class="deviceList_title_text" style="width:15%">
                  {{dictionary(item.memberStatus,'meeting_member_status')}}
                </div>
                <div style="width:24%;">
                  <div v-if="meetingStatus==2">
                    <div v-if="item.imei == createBy&&item.imei == userName">
                      <div v-if="item.devStatus==3||item.devStatus==4||item.devStatus==5||item.devStatus==1">
                        <div style="width:100%;justify-content: center;" class="deviceList_operate">
                          <div class="deviceList_title_text1" @click="speak('request')"
                            v-if="item.memberStatus!=3&&item.memberStatus!=1&&item.memberStatus!=4&&item.memberStatus!=5">
                            <div>开始发言</div>
                          </div>
                          <div class="deviceList_title_text1" @click="speak('request')" v-if="item.memberStatus==3">
                            <div>结束发言</div>
                          </div>
                          <div class="deviceList_title_text1" @click="outMeeting" v-if="item.memberStatus==2">离开会议
                          </div>
                          <div class="deviceList_title_text1" @click="inMeeting" v-if="item.memberStatus==1">进入会议
                          </div>
                        </div>
                      </div>
                    </div>
                    <div v-if="item.imei != createBy">
                      <div v-if="item.devStatus==3||item.devStatus==4||item.devStatus==5||item.devStatus==1">
                        <div style="width:100%;justify-content: center;" class="deviceList_operate"
                          v-show="createBy==userName&&isInOrOutMeeting&&deviceList[0].memberStatus==2">
                          <div class="deviceList_title_text1" @click="speak('call',item.imei,'start')"
                            v-if="item.memberStatus!=3&&item.memberStatus!=1&&item.memberStatus!=4&&item.memberStatus!=5">
                            <div>点名发言</div>
                          </div>
                          <div class="deviceList_title_text1" @click="speak('call',item.imei,'end')"
                            v-if="item.memberStatus==3">
                            <div>结束发言</div>
                          </div>
                          <div class="deviceList_title_text1" @click="inOrOut('out',item.imei)"
                            v-if="item.memberStatus==2">请离会议</div>
                          <div class="deviceList_title_text1" @click="inOrOut('in',item.imei)"
                            v-if="item.memberStatus==1">拉入会议</div>
                        </div>
                      </div>
                    </div>
                  </div>

                </div>
              </div>
            </div>
          </div>
        </div>

      </div>

    </div>
  </div>

</template>
<script>
  import Header from "../home/header/index";
  import Footer from "../home/footer/index";
  import webSocketClass from "@/utils/webSocket";
  import { postWarnStatus } from "@/api/AlarmRecord";
  import organizationTree from "./deviceTree/organizationTree"
  import { devicetree } from "@/api/system/deviceTree";
  import { timestampToTime } from "../../../utils/time.js"
  import { addGroup, delGroup, getGroup, listGroup, updateGroup, updateMeetingStatus, selectDeviceGroupDetailList, deviceRequestTalking, inOrOutMeeting } from "@/api/system/group";
  import { listData } from "@/api/system/dict/data";
  import Ctibar from './AgentBar/ctibar.js';
  var audio = document.getElementById('audio');

  var constraints = {
    audio: true,
    video: true,
    mandatory: {
      maxWidth: 640,
      maxHeight: 360
    }
  };
  URL = window.URL || window.webkitURL;

  var eventHandlers = {
    'progress': function (e) {
      console.log('call is in progress');
    },
    'failed': function (e) {
      console.log('call failed: ', e);
    },
    'ended': function (e) {
      console.log('call ended : ', e);
    },
    'confirmed': function (e) {
      console.log('call confirmed');
    }
  };
  export default {
    dicts: ["warn_type"],
    components: {
      Header,
      Footer,
      organizationTree
    },
    data() {
    
      return {
     
        // 以下群组
        isGroup: false,
        createBy: '',
        isInOrOutMeeting: false,
        isSpeak: false,
        itemId: '',
        meetingStatus: '',
        groupDetail: {},
        isMike: false,
        deviceList: null,
        meetingName1: "会议未开始",
        meetingName: "会议中",
        groupName: "",
        dataList: null,
        userId: JSON.parse(sessionStorage.getItem("userInfo")).userId,
        userName: JSON.parse(sessionStorage.getItem("userInfo")).userName,
        // 查询参数
        queryParams: {
          pageNum: 1,
          pageSize: 10,
          deptId: null,
          planName: null,
          status: null,
        },
        total: 0,
        deviceTreeList: [],
        title: "",
        num: 1,
        groupId: '',
        dictionaryList: [],
        websocket: null,

        //初始化SDK所需要的配置
        config: {
          host: '39.152.2.103',
          port: '5066',
          proto: false,
          extNo: '',
          extPwd: '20181231',
          autoRegister: true,
          debug: true,
          //stunServer: 'stun.1.google.com',   可自行修改使用stun服务器地址
          stateEventListener: this.stateEventListener
        },
        //坐席分机号
        agentNo: '',
        //客户号码
        customerNo: '',
        //拨号弹窗
        showDial: false,
        //转接弹窗
        showTransferDial: false,
        //转接号码
        transNum: '',
        numList: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#',],
        agentStatus: 'DISCONNECTED',
        statusMap: {
          CONNECTED: '已连接',
          DISCONNECTED: '网络断开',
          REGISTERED: '已注册',
          UNREGISTERED: '未注册',
          REGISTER_FAILED: '注册失败',
          IN_CALL: '通话中',
          INCOMING_CALL: '来电振铃',
          OUTGOING_CALL: '外呼振铃',
          HOLD: '保持中',
          CALL_END: '通话结束'
        },
        timer: null,
        timerString: '00:00:00',
        outNum: '',
        isHold: true
      };
    },
    computed: {
      classObject() {
        const bool1 = this.alarmArr.length > 1;
        const bool2 = this.alarmArr.length === 1;
        return {
          tanchuangbox: true,
          "tanchuangbox-height-multi": bool1,
          "tanchuangbox-height-single": bool2,
        };
      },
    },
    mounted() {

      this.init()
  
    },
    methods: {

      // 以下群组
      inMeeting() {
        this.loadingFun()
        var body = {
          devImei: this.userName,
          flag: 'in',
          groupId: this.groupId,
        }

        inOrOutMeeting(body).then(response => {
          if (response.data.success) {
          } else {
            this.$modal.msgError(response.data.message);
          }
          this.loading.close();
        },
          error => {
            this.loading.close();
          });




      },
      outMeeting() {
        this.loadingFun()
        var body = {
          devImei: this.userName,
          flag: 'out',
          groupId: this.groupId,
        }

        inOrOutMeeting(body).then(response => {

          if (response.data.message == '已离开!') {

          } else {
            this.$modal.msgError(response.data.message);
          }
          this.loading.close();
        },
          error => {
            this.loading.close();
          });




      },

      inOrOut(flag, devImei) {
        this.loadingFun()
        var body = {
          devImei: devImei,
          flag: flag,
          groupId: this.groupId,
        }
        inOrOutMeeting(body).then(response => {
          console.log("123123123", response)
          if (response.data.success) {
            this.$modal.msgSuccess(response.data.message);
          } else {
            this.$modal.msgError(response.data.message);
          }
          this.loading.close();
        },
          error => {
            this.loading.close();
          });
      },
      loadingFun() {
        this.loading = this.$loading({
          lock: true,
          text: 'Loading',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)'
        });
      },
      speak(type, devImei, flag) {
        this.loadingFun()
        var flagg = ''
        if (type == 'request') {
          flagg = !this.isSpeak ? 'start' : 'end'
          devImei = this.userName
        } else {
          flagg = flag
        }
        var body = {
          devImei: devImei,
          flag: flagg,
          groupId: this.groupId,
          type: type,
        }
        deviceRequestTalking(body).then(response => {
          if (response.data.success) {
            if (type == 'request') {

            }
            this.$modal.msgSuccess(response.data.message);
            //恢复发言
          } else {
            this.$modal.msgError(response.data.message);
            this.hold()
          }
          this.loading.close();
        },
          error => {
            this.loading.close();
          });
      },
      // 1、进入元素
      mouseover(index) {
        this.itemId = index
      },
      // 4、离开元素
      mouseout() {
        this.itemId = ''
      },
      ws() {
        console.log('{"groupId":' + this.groupId + '}')
        this.websocket.webSocketSendMsg('{"groupId":' + this.groupId + '}')

      },
      devStatus(devStatus) {
        for (const i in this.deviceList) {
          if (this.deviceList[i].devStatus == devStatus) {
            this.deviceList[i].devStatus = devStatus
          }
        }
      },
      dictionary(e, type, imei) {
        for (const i in this.dictionaryList) {
          if (this.createBy == imei && e == 4) {
            return '在线'
          }
          if (this.dictionaryList[i].dictValue == e && this.dictionaryList[i].dictType == type) {
            return this.dictionaryList[i].dictLabel
          }
        }
      },
      handleAdd() {
        // this.reset();
        // this.open = true;
        this.title = "添加群组";
      },

      tableRowClassName({ row, rowIndex }) {

        return 'photo';
      },
      rowClass({ row, rowIndex }) {


        return 'text-align: center;background-color: #1A1D30;color: #fff'

      },
      meetingAll() {
        var meetingStatusAll = 2
        var params = {
          groupId: this.groupId,
          meetingStatus: meetingStatusAll
        }
        updateMeetingStatus(params).then(response => {
          if (response.code == 200) {

            this.$modal.msgSuccess(response.msg);
          } else {
            this.$modal.msgError(response.msg);

          }
        });
      },

      meeting() {
        this.loadingFun()
        if (this.deviceList != null) {
          console.log('meetingStatusmeetingStatus', this.meetingStatus)
          var params = {
            groupId: this.groupId,
            meetingStatus: this.meetingStatus == 1 ? 2 : 1
          }
          updateMeetingStatus(params).then(response => {
            setTimeout(() => {
              if (response.code == 200) {
                if (this.meetingStatus == 1) {
                  if (sessionStorage.getItem('groupId') == this.groupId) {
                    this.handleAgentBarBtnClick('hangup')
                    sessionStorage.setItem('groupId', '')
                  }
                  this.isInOrOutMeeting = false
                  this.isHold = true

                  this.loading.close()
                }
              }
            }, 5000);

          });
        }
      },
      //孙组件向父组件传递数据
      wsMeetingStatus(isInterface) {
        console.log('isMeeting状态:', isInterface)
        this.meetingStatus = isInterface
      },
      groupDeviceItemClick(item) {
        // this.handleAgentBarBtnClick('hangup')
        this.isSpeak = false
        this.deviceList = []
        this.groupId = item.id
        this.meetingStatus = item.meetingStatus
        this.groupName = item.groupName
        this.createBy = item.createBy
        this.item = item
        var params = { groupInfoId: item.id, pageNum: 0, pageSize: 0, }
        selectDeviceGroupDetailList(params).then(response => {
          this.deviceList = response.rows
          this.total = this.deviceList.length
          this.ws()
          if (this.deviceList[0].memberStatus == 3 &&
            this.deviceList[0].imei == this.userName) {
            this.isSpeak = true;
          }
          if (
            this.deviceList[0].memberStatus == 2 &&
            this.deviceList[0].imei == this.userName
          ) {
            if (sessionStorage.getItem("groupId") == this.groupId) {
              this.isInOrOutMeeting = true
              return
            }
            if (sessionStorage.getItem("groupId")) {
            } else {
              this.call()
            }
          }
        });

      },




      onClickDialOutside(event) {
        console.log(event)
        this.showDial = false
      },
      onClickTransDialOutside(event) {
        this.showTransferDial = false;
      },
      handleAgentBarBtnClick(name) {
        console.log(name + '当前')
        if (name === 'login') {
          this.login();
        } else if (name === 'logout') {
          this.logout();
        } else if (name === 'answer') {
          this.answer();
        } else if (name === 'hangup') {
          this.hangup();
        } else if (name === 'makecall') {
          this.makeCall('9*' + this.groupId)
        } else if (name === 'hold') {
          this.hold();
        } else if (name === 'unhold') {
          this.unhold();
        } else if (name === 'transfer') {
          this.transfer(this.transNum)
        }
      },

      login() {
        this.init()
        Ctibar.register()
      },
      logout() {
        Ctibar.unregister()
      },
      makeCall(phone) {
        if (phone === "" || phone === undefined) {
          console.error('无效的号码,请重新输入!');
          return
        }
        Ctibar.makecall(phone);

      },
      hold() {
        Ctibar.hold();

      },
      unhold() {
        Ctibar.unhold();
      },
      answer() {
        Ctibar.answer();
      },
      hangup() {
        Ctibar.hangup();
      },
      transfer(phone) {
        console.info("触发转接", phone)
        Ctibar.transfer(phone);
      },
      //外呼拨号盘
      handleDialBtnClick(val) {
        this.outNum += val;
        this.$refs.outNumInput.focus();
      },
      //转接拨号盘
      handleTransDialBtnClick(val) {
        this.transNum += val;
        this.$refs.transNumInput.focus();
      },

      //参数为时间差秒数,返回这两个时间差并格式化
      computeTimeDiff(diff) {
        diff = Math.round(diff / 1000);
        let hour = Math.floor(diff / 3600).toString().padStart(2, '0');
        let min = Math.floor((diff - hour * 3600) / 60).toString().padStart(2, '0');
        let sec = (diff % 60).toString().padStart(2, '0');
        return hour + ':' + min + ':' + sec;
      },
      //重置时间
      restoreTime(origin) {
        clearInterval(this.timer);
        this.timerString = '00:00:00';
        this.timer = setInterval(() => {
          this.timerString = this.computeTimeDiff(new Date().getTime() - origin);
        }, 1000);
      },
      //状态变更回调
      stateEventListener(event, data) {
        //debug使用
        console.log('当前event为: ' + event + ', 当前data为: ' + JSON.stringify(data))

        this.agentStatus = event
        let origin = new Date().getTime();
        switch (event) {
          case "CONNECTED":
            this.agentNo = data.localAgent
            this.restoreTime(origin);
            break;

          case "DISCONNECTED":
            this.restoreTime(origin);
            break;

          case "UNREGISTERED":
            this.restoreTime(origin);
            break;

          case "OUTGOING_CALL":
            this.customerNo = data.otherLegNumber;
            this.restoreTime(origin);
            break;

          case "INCOMING_CALL":
            this.customerNo = data.otherLegNumber;
            //播放来电振铃音
            this.playRingMedia();
            this.restoreTime(origin);
            break;

          case "IN_CALL":
            this.stopPlayRingMedia();
            this.restoreTime(origin);

            // this.timer = setInterval(() => {
            // }, 1000);
            console.log('当前是否hold', this.isHold)
            if (this.isHold) {
              setTimeout(() => {
                this.handleAgentBarBtnClick('hold')
                this.isHold = false
                // 方法区
              }, 500);
            }

            break;

          case "CALL_END":
            this.stopPlayRingMedia();
            //挂机铃声
            this.playHangupMedia()
            this.restoreTime(origin);
            break;

          default:

        }
      },

      //播放挂机铃声
      playHangupMedia() {
        const _this = this;
        var hangupAudio = document.getElementById("hangupMediaAudioId")
        if (!hangupAudio) {
          hangupAudio = document.createElement('audio');
          hangupAudio.id = 'hangupMediaAudioId';
          hangupAudio.hidden = true;
          hangupAudio.src = 'wav/hangup.wav'
          document.body.appendChild(hangupAudio);
        }
        hangupAudio.play();
      },
      //播放来电振铃
      playRingMedia() {
        const _this = this;
        _this.stopPlayRingMedia();

        var ringAudio = document.getElementById("ringMediaAudioId")
        if (!ringAudio) {
          ringAudio = document.createElement('audio');
          ringAudio.id = 'ringMediaAudioId';
          ringAudio.hidden = true;
          ringAudio.src = 'wav/ring.wav';
          ringAudio.loop = 'loop';
          document.body.appendChild(ringAudio);
        }
        ringAudio.play();
      },
      //停止播放来电振铃
      stopPlayRingMedia() {
        const _this = this;
        var ringAudio = document.getElementById("ringMediaAudioId");
        if (ringAudio) {
          document.body.removeChild(ringAudio);
        }
      },
      //初始化方法
      init() {
        this.config.extNo = this.userName
        if (sessionStorage.getItem("freeSwitchWs") != null) {
          this.config.host = sessionStorage.getItem("freeSwitchWs").split(":")[0]
          this.config.port = sessionStorage.getItem("freeSwitchWs").split(":")[1]
        }
        Ctibar.initSDK(this.config)


        let url = `/ws/` + this.userId + '/group/device/push';
        this.websocket = new webSocketClass(url)
        this.websocket.getWebSocketMsg(evt => {
          // 客户端接收服务端返回的数据
          var data = JSON.parse(evt.data);
          console.log("websocket返回的数据123:", data);
          switch (data.flag) {
            case "group"://会议状态
              this.$refs.organizationTree.setGroupList(data, this.groupId)
              console.log("websocket返回的数据123:", data);
              break
            case "memberStatus"://成员状态
              for (const i in this.deviceList) {
                console.log('123123132', data.member);
                if (this.deviceList[i].imei == data.member) {
                  this.deviceList[i].devStatus = data.memberStatus;
                }
              }
              break
            case "memberMeetingStatus"://成员会议状态
              for (const i in this.deviceList) {
                if (this.deviceList[i].imei == data.member) {
                  this.deviceList[i].memberStatus = data.memberMeetingStatus;
                }
              }
              if (this.groupId == data.groupId && this.userName == data.member && this.createBy == data.member) {
                if (data.memberMeetingStatus == 3) {
                  this.isSpeak = true;
                  this.unhold()
                  console.log('数据this.isInOrOutMeeting1111', this.isSpeak)
                } else {
                  this.isSpeak = false;
                  this.hold()
                }
              }

              if (data.memberMeetingStatus == 4) {
                this.loading.close()
              }
              if (!this.isInOrOutMeeting && this.groupId == data.groupId && this.userName == data.member && this.createBy == data.member && data.memberMeetingStatus == 2) {
                this.hold()
                if (sessionStorage.getItem("groupId") &&
                  sessionStorage.getItem("groupId") !== this.groupId) {
                } else {
                  console.log('this.isInOrOutMeeting1111', data.memberMeetingStatus)
                  this.call()
                }
              }
              if (this.createBy == data.member && data.memberMeetingStatus == 1 && this.userName == data.member) {
                this.handleAgentBarBtnClick('hangup')
                sessionStorage.setItem("groupId", '')
                this.isInOrOutMeeting = false
                this.isSpeak = false
                this.isHold = true
              }
              break

          }
        })
      },
      setGroup(group) {
        this.isGroup = group
     
   
      },
      call() {
        this.handleAgentBarBtnClick('makecall')
        sessionStorage.setItem("groupId", this.groupId)
        this.isInOrOutMeeting = true
        if (this.deviceList[0].imei != this.createBy) {
          // this.groupDeviceItemClick(this.item)
        }
        var that = this
        window.onbeforeunload = (e) => {
          console.log('this.isInOrOutMeeting', that.isInOrOutMeeting)
          if (that.isInOrOutMeeting) {
            that.handleAgentBarBtnClick('hangup')
            sessionStorage.setItem("groupId", '')
            that.websocket.closeSocket()
            window.onbeforeunload = null
          }
        }
        this.loading.close()
        this.isInOrOutMeeting = true
        this.isHold = true
      }
    },
    beforeDestroy() {  //进行监听销毁
      console.log('1231232131232132131,', this.isInOrOutMeeting)
      if (this.isInOrOutMeeting) {
        this.handleAgentBarBtnClick('hangup')
        sessionStorage.setItem("groupId", '')
        this.websocket.closeSocket()
        window.onbeforeunload = null
      }
    },



  };
</script>


http://www.niftyadmin.cn/n/455125.html

相关文章

数据库字段名称为关键字,mybatisplus中的解决方法

最近在使用mybatisplus批量插入数据的时候&#xff0c;报了一个错误&#xff0c;代码提示语法错误&#xff1a; ### Error updating database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your …

sql数据处理,各种条件语句

--数据库多表的连接查询 一、外连接 1.左连接 left join / left outer join 左外连接包含left join 左表里的所有行&#xff0c;若左表在右表没有匹配&#xff0c;则结果中对应 行的右表部分全部为空 select * from student left join course on student.ID course.ID …

安卓蓝牙L2CAP协议简介及报文格式

概述 逻辑链路控制和适配协议&#xff08;Logical Link Control and Adaptation Protocol&#xff0c;L2CAP&#xff09;是蓝牙的核心协议&#xff0c;负责适配基带中的上层协议。它同链路管理器并行工作&#xff0c;向上层协议提供定向连接的和无连接的数据业务。L2CAP具有分…

双向可控硅控制后续篇:过零检测电路、丢波、斩波、定时

概念讲解 当我们在使用AC负载的时候&#xff0c;为了能较好的控制负载工作功率&#xff0c;需要用到继电器、可控硅等对负载进行工作与断开的控制&#xff0c;从而将功率维持在所需的大小上&#xff0c;之前介绍双向可控硅的文章也讲了其控制方式主要有&#xff1a;定时、丢波…

(六)关于Linux中服务器磁盘爆满问题的几个解决方案

文章目录 一、现象二、现象产生的原因三、问题的定位过程1.检查业务文件存放位置是否合理2.检查服务器系统盘空间是否分配过小3.对服务器磁盘占用情况进行分析4.若以上方案都解决不了内存占用过高问题&#xff0c;则考虑是否是服务器中存在其他容器产生了一些文件&#xff0c;比…

【操作系统】CPU调度

目录 1.什么叫调度 2.调度的目标 3.进程调度方式 4.闲逛进程 5.典型的调度算法 5.1先来先服务(FCFS)调度算法 5.2短作业优先(SJF) 调度算法 5.3优先级调度算法 5.4高响应比优先调度算法 5.5时间片轮转调度算法 5.6多级队列调度算法 5.7多级反馈队列调度算法(融合了前…

基础篇:新手使用vs code新建go项目(从0开始到运行)

学习新语言&#xff0c;搭建新环境。在网上找了一些教程&#xff0c;感觉还是写一个比较详细的方便以后自己使用。其实vs code没有新建项目这个功能&#xff0c;具体怎么运行go语言的项目请看下文。 一、下载GO安装包 1.点击go安装包下载链接下载相应的版本&#xff08;本次下…

PostgreSQL的优势:为何它成为主流数据库管理系统

PostgreSQL的优势&#xff1a;为何它成为主流数据库管理系统 Stack Overflow 2023年报告PostgreSQL和MySQL同异我们在开发中如何选择PostgreSQL和MySQL呢&#xff1f; 摘要&#xff1a;本文主要比较了PostgreSQL和MySQL这两个流行的关系型数据库管理系统。我们首先介绍了它们的…