Harmony Ble蓝牙App(二)连接与发现服务

news2024/11/15 9:05:41

Ble蓝牙App(二)连接与发现服务

  • 前言
  • 正文
    • 一、BlePeripheral回调
    • 二、连接和断连
    • 三、连接状态回调
    • 四、发现服务
    • 五、服务提供者
    • 六、显示服务
    • 七、源码

前言

  在上一篇中我们进行扫描设备的处理,本文中进行连接和发现服务的数据处理,运行效果图如下所示:
在这里插入图片描述

正文

  现在我们在ScanSlice扫描设备,选中一个设备进入MainAbilitySlice,下面要对选中的设备进行处理,首先我们来做连接。

一、BlePeripheral回调

  在之前我们写了一个BleCore,这里面是对扫描的封装,那么对于连接来说我们同样可以封装到这里,我们可以在BleCore中写一个BleDeviceCallback 类,继承自BlePeripheralCallback, 代码如下所示:

    private class BleDeviceCallback extends BlePeripheralCallback {

        /**
         * 连接状态改变
         * @param connectionState 状态码
         */
        @Override
        public void connectionStateChangeEvent(int connectionState) {

        }

        /**
         * 发现服务
         * @param status 状态
         */
        @Override
        public void servicesDiscoveredEvent(int status) {

        }
    }

  因为本文要做的事情是连接和发现服务,所以我们就先重写这两个方法,注意一点的是,蓝牙的操作都是在子线程中进行的,如果我们需要知道当前是否连接,则需要写一个接口用于回调到MainAbilitySlice中,在core包下新建一个BleCallback接口,代码如下所示:

public interface BleCallback {
    
    /**
     * 设备的所有信息
     *
     * @param info 信息
     */
    void deviceInfo(String info);

    /**
     * 连接状态
     *
     * @param state true or false
     */
    void connectionStateChange(boolean state);

    /**
     * 发现服务
     *
     * @param services 服务列表
     */
    void servicesDiscovered(List<GattService> services);
}

  接口中定义了三个方法,通过注释我们清晰的知道都是什么作用,这里着重介绍第一个函数,这个函数会显示设备各个时候的状态信息,从连接之后的所有动作,如果我们需要保存设备的操作日志的话,可以通过这个来进行处理保存。

然后回到BleCore,声明变量和设置接口回调的方法:

    private final BleDeviceCallback mBleDeviceCallback;
    private BleCallback bleCallback;
    private BlePeripheralDevice mDevice;
    private boolean mIsConnected;
	
    public BleCore(Context context) {
        ...
        //蓝牙设备类
        mBleDeviceCallback = new BleDeviceCallback();
    }	
    
    public void setBleCallback(BleCallback bleCallback) {
        this.bleCallback = bleCallback;
    }

    private void deviceInfo(String info) {
        if (bleCallback != null) {
            bleCallback.deviceInfo(info);
        }
    }

    private void connectState(boolean state) {
        mIsConnected = state;
        if (bleCallback != null) {
            bleCallback.connectionStateChange(state);
        }
    }
    
    public void setDevice(BlePeripheralDevice device) {
        mDevice = device;
    }

这里就是对设备信息和连接做了一个处理,下面我们增加连接和断连的方法。

二、连接和断连

在BleCore中增加如下代码:

    public boolean isConnected() {
        return mIsConnected;
    }
    
    public void connect() {
        if (mDevice == null) return;
        deviceInfo("连接设备...");
        mDevice.connect(false, mBleDeviceCallback);
    }

    public void disconnect() {
        if (mDevice == null) return;
        deviceInfo("断开连接设备...");
        mDevice.disconnect();
    }

连接与断开连接,调用时会触发connectionStateChangeEvent()方法。

三、连接状态回调

下面修改这个方法的代码,如下所示:

        @Override
        public void connectionStateChangeEvent(int connectionState) {
            String address = mDevice.getDeviceAddr();
            if (connectionState == ProfileBase.STATE_CONNECTED) {
                deviceInfo("连接成功:" + address);
                connectState(true);
            } else if (connectionState == ProfileBase.STATE_DISCONNECTED) {
                deviceInfo("断开连接成功:" + address);
                connectState(false);
                mDevice.close();
            }
        }

  在回调中,连接成功和断开连接都会有一个对应的状态码,通过状态回调到接口函数中,然后回到MainAbilitySlice中使用一下这个回调,首先我们修改一下ability_main.xml中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:background_element="$color:bg_color"
    ohos:orientation="vertical">

    <DirectionalLayout
        ohos:height="50vp"
        ohos:width="match_parent"
        ohos:alignment="vertical_center"
        ohos:background_element="$color:blue"
        ohos:orientation="horizontal"
        ohos:start_padding="12vp">

        <Text
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:text="操作设备"
            ohos:text_color="#FFF"
            ohos:text_font="HwChinese-medium"
            ohos:text_size="18fp"
            ohos:weight="1"/>

        <Text
            ohos:id="$+id:tx_disconnect"
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:end_margin="6vp"
            ohos:padding="8vp"
            ohos:text="断开连接"
            ohos:text_color="#FFF"
            ohos:text_size="14fp"/>

    </DirectionalLayout>

    <Text
        ohos:id="$+id:tx_device_info"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:text="设备信息"
        ohos:padding="12vp"
        ohos:text_size="14fp"/>

    <ListContainer
        ohos:id="$+id:lc_service"
        ohos:height="match_parent"
        ohos:width="match_parent"/>

</DirectionalLayout>

  在XML中只增加了两个Text,分别用于断连和显示设备状态,下面我们写Slice之间的跳转,也就是从ScanSlice跳转到MainAbilitySlice。首先我们在MainAbility中增加action,代码如下所示:

public class MainAbility extends Ability {
    @Override
    public void onStart(Intent intent) {
        ...

        // add action for ability
        addActionRoute("action.main", MainAbilitySlice.class.getName());
    }
}

然后去config.json中增加配置,如下图所示:

在这里插入图片描述

然后我们回到ScanSlice中,在Item的点击事件中添加如下代码:

        //列表item点击监听
        lcDevice.setItemClickedListener((listContainer, component, position, id) -> {
            //设置设备
            bleCore.setDevice(mList.get(position).getDevice());
            Intent jumpIntent = new Intent();
            Operation operation = new Intent.OperationBuilder()
                    .withAction("action.main")
                    .withDeviceId("")
                    .withBundleName("com.llw.ble")
                    .withAbilityName("com.llw.ble.MainAbility")
                    .build();
            jumpIntent.setOperation(operation);
            startAbility(jumpIntent);
        });

  这里跳转的代码比较多,跳转之前设置了设备进去,这种方式也是官方推荐的方式。然后我们修改MainAbility中的代码,如下所示:

public class MainAbilitySlice extends AbilitySlice implements BleCallback {

    private static final String TAG = MainAbilitySlice.class.getSimpleName();
    private Text txDisconnect;
    private Text txDeviceInfo;
    private ListContainer lcService;

    private BleCore bleCore;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);

        txDisconnect = (Text) findComponentById(ResourceTable.Id_tx_disconnect);
        txDeviceInfo = (Text) findComponentById(ResourceTable.Id_tx_device_info);
        lcService = (ListContainer) findComponentById(ResourceTable.Id_lc_service);

        bleCore = BleApp.getBleCore();
        bleCore.setBleCallback(this);
        //连接设备
        bleCore.connect();

        txDisconnect.setClickedListener(component -> {
            if (bleCore.isConnected()) {
                bleCore.disconnect();
            } else {
                bleCore.connect();
            }
        });
    }

    @Override
    public void deviceInfo(String info) {
        getUITaskDispatcher().asyncDispatch(() -> {
            LogUtils.LogD(TAG, info);
            txDeviceInfo.setText(info);
        });
    }

    @Override
    public void connectionStateChange(boolean state) {
        getUITaskDispatcher().asyncDispatch(() -> txDisconnect.setText(state ? "断开连接" : "连接"));
    }

    @Override
    public void servicesDiscovered(List<GattService> services) {

    }
}

  使用BleCoreconnect()方法进行连接设备,在onStart()方法中进行BleCore的赋值,然后设置Ble的回调,实现BleCallback接口,重写里面的函数,当连接成功之后会通过回调deviceInfo()得到设备状态,因为是子线程所以在ui线程中渲染UI。而connectionStateChange()函数,回调连接成功或者失败,如果成功则为ture,就显示txDisconnect控件,此时连接成功,点击这个txDisconnect就会断开连接,点击监听就在onStart()中写好了,下面我们运行一下看看效果。

在这里插入图片描述

从这个效果图来看,我们连接成功之后有状态,点击断开连接也会有状态改变,那么连接就写好了。

四、发现服务

  连接写好了,下面可以写发现服务了,我们可以在连接成功的处理中进行发现服务,下面我们修改一下BleDeviceCallback中的connectionStateChangeEvent()方法中的代码,如下图所示:

在这里插入图片描述

通过mDevice.discoverServices()进行发现服务的动作,在此之前通过deviceInfo()设置当前的动作状态,发现服务执行会触发servicesDiscoveredEvent()回调,在这个回调中我们可以回调到页面,修改代码如下所示:

        @Override
        public void servicesDiscoveredEvent(int status) {
            if (status == BlePeripheralDevice.OPERATION_SUCC) {
                deviceInfo("发现" + mDevice.getServices().size() + "服务");
                if (bleCallback != null) {
                    bleCallback.servicesDiscovered(mDevice.getServices());
                }
            }
        }

  在回调中设置发现服务的个数,然后回调,因为服务是多个的,那么下面我们就需要使用一个列表是装载服务,之前我们就已经在ability_main.xml中写好了。

五、服务提供者

  要显示服务列表数据,首先需要一个提供者,而提供者又需要一个item去渲染数据,下面我们在layout下创建一个item_service.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_content"
    ohos:width="match_parent"
    ohos:background_element="$color:white"
    ohos:bottom_margin="2vp"
    ohos:bottom_padding="8vp"
    ohos:end_padding="16vp"
    ohos:start_padding="16vp"
    ohos:top_padding="8vp">

    <Text
        ohos:id="$+id:tx_service_name"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text="服务"
        ohos:text_size="16fp"/>

    <Button
        ohos:id="$+id:tx_uuid_title"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:below="$id:tx_service_name"
        ohos:text="UUID:"
        ohos:text_color="$color:gray"
        ohos:text_size="16fp"
        ohos:top_margin="2vp"/>

    <Button
        ohos:id="$+id:tx_uuid"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:below="$id:tx_service_name"
        ohos:end_of="$id:tx_uuid_title"
        ohos:text="UUID"
        ohos:text_size="16fp"
        ohos:top_margin="2vp"/>

    <Button
        ohos:id="$+id:tx_service_info"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:below="$id:tx_uuid_title"
        ohos:text="PRIMARY SERVICE"
        ohos:text_color="$color:gray"
        ohos:text_size="16fp"
        ohos:top_margin="2vp"/>

</DependentLayout>

下面我们在ble包下新建一个BleUtils类,代码如下所示:

public class BleUtils {

    public static final String generic = "-0000-1000-8000-00805F9B34FB";

    public static String getServiceUUID(UUID uuid) {
        return "0x" + uuid.toString().substring(4, 8).toUpperCase();
    }

    /**
     * 获取蓝牙服务名称
     *
     * @param uuid UUID
     */
    public static String getServiceName(UUID uuid) {
        String targetUuid = getServiceUUID(uuid);
        switch (targetUuid) {
            case "0x1800":
                return "Generic Access service";
            case "0x1801":
                return "Generic Attribute service";
            case "0x1802":
                return "Immediate Alert service";
            case "0x1803":
                return "Link Loss service";
            case "0x1804":
                return "Tx Power service";
            case "0x1805":
                return "Current Time service";
            case "0x1806":
                return "Reference Time Update service";
            case "0x1807":
                return "Next DST Change service";
            case "0x1808":
                return "Glucose service";
            case "0x1809":
                return "Health Thermometer service";
            case "0x180A":
                return "Device Information service";
            case "0x180D":
                return "Heart Rate service";
            case "0x180E":
                return "Phone Alert Status service";
            case "0x180F":
                return "Battery service";
            case "0x1810":
                return "Blood Pressure service";
            case "0x1811":
                return "Alert Notification service";
            case "0x1812":
                return "Human Interface Device service";
            case "0x1813":
                return "Scan Parameters service";
            case "0x1814":
                return "Running Speed and Cadence service";
            case "0x1815":
                return "Automation IO service";
            case "0x1816":
                return "Cycling Speed and Cadence service";
            case "0x1818":
                return "Cycling Power service";
            case "0x1819":
                return "Location and Navigation service";
            case "0x181A":
                return "Environmental Sensing service";
            case "0x181B":
                return "Body Composition service";
            case "0x181C":
                return "User Data service";
            case "0x181D":
                return "Weight Scale service";
            case "0x181E":
                return "Bond Management service";
            case "0x181F":
                return "Continuous Glucose Monitoring service";
            case "0x1820":
                return "Internet Protocol Support service";
            case "0x1821":
                return "Indoor Positioning service";
            case "0x1822":
                return "Pulse Oximeter service";
            case "0x1823":
                return "HTTP Proxy service";
            case "0x1824":
                return "Transport Discovery service";
            case "0x1825":
                return "Object Transfer service";
            case "0x1826":
                return "Fitness Machine service";
            case "0x1827":
                return "Mesh Provisioning service";
            case "0x1828":
                return "Mesh Proxy service";
            case "0x1829":
                return "Reconnection Configuration service";
            case "0x183A":
                return "Insulin Delivery service";
            case "0x183B":
                return "Binary Sensor service";
            case "0x183C":
                return "Emergency Configuration service";
            case "0x183D":
                return "Authorization Control service";
            case "0x183E":
                return "Physical Activity Monitor service";
            case "0x183F":
                return "Elapsed Time service";
            case "0x1840":
                return "Generic Health Sensor service";
            case "0x1843":
                return "Audio Input Control service";
            case "0x1844":
                return "Volume Control service";
            case "0x1845":
                return "Volume Offset Control service";
            case "0x1846":
                return "Coordinated Set Identification service";
            case "0x1847":
                return "Device Time service";
            case "0x1848":
                return "Media Control service";
            case "0x1849":
                return "Generic Media Control service";
            case "0x184A":
                return "Constant Tone Extension service";
            case "0x184B":
                return "Telephone Bearer service";
            case "0x184C":
                return "Generic Telephone Bearer service";
            case "0x184D":
                return "Microphone Control service";
            case "0x184E":
                return "Audio Stream Control service";
            case "0x184F":
                return "Broadcast Audio Scan service";
            case "0x1850":
                return " Published Audio Capabilities service";
            case "0x1851":
                return "Basic Audio Announcement service";
            case "0x1852":
                return "Broadcast Audio Announcement service";
            case "0x1853":
                return "Common Audio service";
            case "0x1854":
                return "Hearing Access service";
            case "0x1855":
                return "Telephony and Media Audio service";
            case "0x1856":
                return "Public Broadcast Announcement service";
            case "0x1857":
                return "Electronic Shelf Label service";
            default:
                return "Unknown Service";
        }
    }
}

  这里需要说明一下蓝牙的UUID,蓝牙UUID(Universally Unique Identifier)是用于唯一标识蓝牙设备和服务的一种标识符。它是一个128位长的数字,在蓝牙通信中起到唯一标识的作用。蓝牙UUID按照标准分为两种类型:

  1. 16位UUID:这些UUID通常用于蓝牙标准定义的一些通用服务和特性。例如,设备名称服务的UUID是 00001800-0000-1000-8000-00805F9B34FB。

  2. 128位UUID:这些UUID通常用于自定义的服务和特性,以确保全球唯一性。可以自行生成一个128位的UUID作为自定义的服务或特性标识。例如,一个自定义的服务UUID可以是 0000XXXX-0000-1000-8000-00805F9B34FB,其中的 XXXX 部分可以是任意的16进制数字。

在蓝牙通信中,设备使用UUID来发布和查找服务以及识别特性。UUID是蓝牙设备之间进行通信时的重要标识,确保了设备和服务的唯一性。

那么getServiceName()中的键你就知道是什么意思了,0x1800就是16进制数字,而对应的值则是SIG定义的,可以参考这个文档:Assigned_Numbers.pdf。如果你的值找不到对应的,那说明它不是SIG规范的,你这个服务UUID就是自己公司自定义的。

下面我们写提供者,在provider包下新建一个ServiceProvider类,代码如下所示:

public class ServiceProvider extends BaseItemProvider {

    private final List<GattService> serviceList;
    private final AbilitySlice slice;

    public ServiceProvider(List<GattService> list, AbilitySlice slice) {
        this.serviceList = list;
        this.slice = slice;
    }

    @Override
    public int getCount() {
        return serviceList == null ? 0 : serviceList.size();
    }

    @Override
    public Object getItem(int position) {
        if (serviceList != null && position >= 0 && position < serviceList.size()) {
            return serviceList.get(position);
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public Component getComponent(int position, Component component, ComponentContainer componentContainer) {
        final Component cpt;
        ServiceHolder holder;

        GattService service = serviceList.get(position);
        if (component == null) {
            cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_service, null, false);
            holder = new ServiceHolder(cpt);
            //将获取到的子组件信息绑定到列表项的实例中
            cpt.setTag(holder);
        } else {
            cpt = component;
            // 从缓存中获取到列表项实例后,直接使用绑定的子组件信息进行数据填充。
            holder = (ServiceHolder) cpt.getTag();
        }

        holder.txServiceName.setText(BleUtils.getServiceName(service.getUuid()));
        holder.txUuid.setText(BleUtils.getServiceUUID(service.getUuid()));
        
        return cpt;
    }

    /**
     * 用于保存列表项的子组件信息
     */
    public static class ServiceHolder {
        Text txServiceName;
        Text txUuid;
        public ServiceHolder(Component component) {
            txServiceName = (Text) component.findComponentById(ResourceTable.Id_tx_service_name);
            txUuid = (Text) component.findComponentById(ResourceTable.Id_tx_uuid);
        }
    }
}

这里的代码就是比较简单的,就是基本的写法,下面回到MainAbilitySlice中进行显示数据。

六、显示服务

首先声明变量:

    private final List<GattService> serviceList = new ArrayList<>();
    private ServiceProvider serviceProvider;

然后实现OnItemClickListener 接口

public class MainAbilitySlice extends AbilitySlice implements BleCallback, ListContainer.ItemClickedListener {

重写onItemClicked()方法,并且增加了一个showMsg,这个Toast的可定制化做的很多。

    @Override
    public void onItemClicked(ListContainer listContainer, Component component, int position, long id) {
        showMsg(serviceList.get(position).getUuid().toString());
    }

    private void showMsg(String msg) {
        ToastDialog toastDialog = new ToastDialog(getContext());
        toastDialog.setSize(DirectionalLayout.LayoutConfig.MATCH_CONTENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT);
        toastDialog.setDuration(2000);
        toastDialog.setText(msg);
        toastDialog.setAlignment(LayoutAlignment.CENTER);
        Text toastText = (Text) toastDialog.getComponent();
        if (toastText != null) {
            toastText.setMultipleLine(true);
            toastText.setTextSize(14, Text.TextSizeType.FP);
            toastText.setTextColor(Color.WHITE);
            toastText.setPadding(40, 20, 40, 20);
            ShapeElement toastBackground = new ShapeElement();
            toastBackground.setRgbColor(new RgbColor(24, 196, 124));
            toastBackground.setCornerRadius(60f);
            toastText.setBackground(toastBackground);
        }
        toastDialog.show();
    }

然后在onStart()方法中初始化服务提供者,代码如下所示:

    @Override
    public void onStart(Intent intent) {
        ...

        serviceProvider = new ServiceProvider(serviceList, this);
        lcService.setItemProvider(serviceProvider);
        lcService.setItemClickedListener(this);
    }

修改servicesDiscovered()方法,代码如下所示:

    @Override
    public void servicesDiscovered(List<GattService> services) {
        getUITaskDispatcher().asyncDispatch(() -> {
            serviceList.clear();
            serviceList.addAll(services);
            serviceProvider.notifyDataChanged();
        });
    }

这里的写法其实和扫描设备哪里如出一辙,下面我们运行一下看看,什么效果。

在这里插入图片描述

七、源码

如果对你有所帮助的话,不妨 StarFork,山高水长,后会有期~

源码地址:HarmonyBle-Java

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1243395.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多线程,线程池,线程的创建,线程池的参数

文章目录 多线程-1 高并发〇、使用多线程的场景1. 为什么使用多线程 1. 线程概述1.1 线程和进程1.2 并发和并行1.3 多线程的优势1.4 程序运行原理1.5 主线程 1.6 线程的 6 种状态2. 线程的创建和启动2.1 Thread类2.2创建线程有哪几种方法2.2.1 继承**Thread**类&#xff0c;重写…

一、用户管理

一、后端数据库初始化 1.1 因为版本问题&#xff0c;始终报错&#xff0c;按照报错信息去查询解决方案&#xff0c;无法解决 灵机一动&#xff1a; 网址&#xff1a; Spring Boot 3.0 升级 实战踩坑记录 - 掘金 (juejin.cn) &#xff11;.&#xff12; 个人配置【运行成功…

1、基础入门——操作系统文件下载反弹SHELL防火墙绕过

名词解释 POC&#xff1a;验证漏洞存在的代码&#xff1b; EXP&#xff1a;利用漏洞的代码&#xff1b; payload&#xff1a;漏洞利用载荷&#xff0c; shellcode&#xff1a;漏洞代码&#xff0c; webshell&#xff1a;特指网站后门&#xff1b; 木马&#xff1a;强调控制…

注意:怎么用JMeter操作MySQL数据库?看完秒懂!

近期用JMeter做接口测试&#xff0c;遇到了一个需要用到数据数据库的场景&#xff1a;一个关于数据报告的页面&#xff0c;需要将数据库里面的数据求和或者取均值之后&#xff0c;展示出来。 如果要断言的话&#xff0c;需要连接数据库&#xff0c;通过写sql语句&#xff0c;将…

OpenLayers实战,WebGL图层如何使用一张拼接合成图片根据坐标切片成单个图片进行渲染

专栏目录: OpenLayers实战进阶专栏目录 前言 本章主要讲解OpenLayers使用WebGL图层的情况下,如何使用一张拼接合成图片根据坐标切片成单个图片进行渲染不同图片到地图上的功能。 为方便讲解原理,本章使用的一张图片是按照横向4等分,纵向两等分规则拼接了6个图标的合成图片…

【分布式】分布式事务及其解决方案

目录 一、分布式事务二、分布式事务的解决方案1. 全局事务&#xff08;1&#xff09;DTP模型&#xff08;2&#xff09; 两阶段提交协议&#xff08;2PC&#xff09;原理二阶段提交的缺点 &#xff08;3&#xff09;三阶段提交协议&#xff08;3PC&#xff09;原理 2. 基于可靠…

Typescript 的 class 类

介绍 1. 类介绍 传统的JavaScript通过函数和基于原型的继承来创建可重用的组件&#xff0c;从ES6开始&#xff0c;JavaScript程序员也可以使用面向对象的方法来创建对象。例如&#xff0c;下列通过class关键词&#xff0c;来声明了一个类&#xff1a;Greeter class Greeter …

shell 脚本 批量 修改 文件名

shell 脚本 批量 修改 文件名 原始文件 你可以使用以下的shell脚本来批量修改文件名&#xff1a; #!/bin/bashinput_dir$1if [ -z "$input_dir" ]; thenecho "Usage: ./rename.sh input_directory"exit 1 ficd "$input_dir" || exitfor file in…

C百题--6.输出C

1.问题描述 输出“C”样式的字符 2.解决思路 1.用printf(&#xff09;逐行输出&#xff1b; 2用循环一部分一部分输出 3.代码实现 #include<stdio.h> int main(){for(int i0;i<5;i){printf("*"); }printf("\n");for(int i0;i<2;i){printf…

【保姆级教程】协同双注意力机制CDAM2与YOLOv7结合:构建高效工地头盔检测系统

1.研究的背景和意义 随着工业化和城市化的快速发展&#xff0c;建筑工地的安全问题日益凸显。在建筑工地中&#xff0c;工人的安全是至关重要的&#xff0c;而工地安全帽的佩戴是保障工人安全的重要措施之一。然而&#xff0c;由于工地环境复杂多变&#xff0c;工人的佩戴情况…

OpenStack云计算平台-启动一个实例

目录 一、创建虚拟网络 ​二、创建m1.nano规格的主机 三、生成一个键值对 四、增加安全组规则 ​五、启动一个实例 1、确定实例选项 2、创建实例 3、使用虚拟控制台访问实例 4、验证能否远程访问实例 一、创建虚拟网络 下面的说明和框图使用示例IP 地址范围。你必须依…

[Linux] shell脚本的函数和数组

一、函数 1.1 函数的定义 函数是脚本的别名 作用&#xff1a;函数可以避免代码重复&#xff0c;可读性强&#xff0c;可以简化脚本。 格式&#xff1a;函数名&#xff08;&#xff09;{脚本} 1.2 如何使用函数 1.定义 2.调用 函数一定要先定义再使用 例子&#xff1a…

Linux 的性能调优的思路

Linux操作系统是一个开源产品&#xff0c;也是一个开源软件的实践和应用平台&#xff0c;在这个平台下有无数的开源软件支撑&#xff0c;我们常见的apache、tomcat、mysql等。 开源软件的最大理念是自由、开放&#xff0c;那么Linux作为一个开源平台&#xff0c;最终要实现的是…

为企业解决设备全生命周期需求,凌雄科技凸显DaaS增长价值

企业成长离不开投资&#xff0c;但毫无疑问的是&#xff0c;投资最有价值的部分在业务。相比之下&#xff0c;诸如办公设备之类的固定资产投资&#xff0c;很容易变成企业现金流的吞噬者。从购买、运维到保养、折旧、回收&#xff0c;现代企业在越来越大的办公设备规模面前&…

通过Whisper模型将YouTube播放列表中的视频转换成高质量文字稿的项目

项目简介 一个通过Whisper模型将YouTube播放列表中的视频转换成高质量文字稿的项目。 这个基于 Python 的工具旨在将 YouTube 视频和播放列表转录为文本。它集成了多种技术&#xff0c;例如用于转录的 Fast-Whisper、用于自然语言处理的 SpaCy 以及用于 GPU 加速的 CUDA&…

机器学习实战-第5章 Logistic回归

Logistic 回归 概述 Logistic 回归 或者叫逻辑回归 虽然名字有回归,但是它是用来做分类的。其主要思想是: 根据现有数据对分类边界线(Decision Boundary)建立回归公式,以此进行分类。 须知概念 Sigmoid 函数 回归 概念 假设现在有一些数据点,我们用一条直线对这些点进行…

Redis深入理解-内核请求处理流程、数据传输协议

Redis 内核级请求处理流程 Redis Server 其实就是 Linux 服务器中的一个进程 主要还是下图的流程 应用先和 server 端建立 TCP 连接建立连接之后&#xff0c;server 端就会有一个与该客户端通信的 socket&#xff0c;客户端的读写请求发送到服务端的 socket那么通过 IO 多路…

C百题--3.求未知数

1.问题描述 一个正整数&#xff0c;它加上100后是一个完全平方数&#xff0c;再加上168又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; 2.解决思路 遍历这个数&#xff0c;让其从1开始&#xff0c;到100000结束 3.代码实现 #include<stdio.h> #include&…

【操作系统】Bochs安装和配置

Bochs是使用C编写的高度可移植开源IA-32&#xff08;X86&#xff09;PC模拟器&#xff0c;能在大多数流行的平台上运行。它包括模拟Intel x86 CPU、常见I/O设备和自定义BIOS。Bochs可以被编译以模拟许多不同的x86 CPU&#xff0c;从386早期到最新的x86-64英特尔和AMD处理器甚至…

区间第k小数 (可持久化线段树、主席树)

题意&#xff1a;多次询问&#xff0c;每次询问某区间的第k小数。 可持久化线段树&#xff1a; 掺杂了一点前缀和的思想&#xff0c;对于每一个1 ~ i 的区间都建一个树&#xff0c;每个节点存的都是一个线段树&#xff0c;值存的是当前区间中初始数组按大小排序后[l, r]之间的…