北航面向对象设计与构造第二单元课程设计总结

杨曜铖22371281 学生 2024-04-20 13:37:22

北航面向对象设计与构造第二单元课程设计总结

目录

  • 北航面向对象设计与构造第二单元课程设计总结
  • 总体架构设计
  • 线程安全与同步的实现
  • 架构分析
  • 设计内容中的稳定和变化
  • 双轿厢避碰:仍是线程同步
  • 调度器迭代设计
  • 指定电梯请求分发
  • 从指定到自主调度
  • 兼容双轿厢电梯新需求
  • 调试经验总结与方法论
  • 调试输出器设计
  • 调试历史(主要)
  • 心得与体会
  • 线程安全
  • 层次化设计

总体架构设计

img

线程安全与同步的实现

线程安全是线程安全类的属性,线程同步包含于具有线程同步功能的方法语义所自然说明的。线程安全和同步都应当被封装在类内部完成,以实现对使用者的透明化。

架构分析

img

设计内容中的稳定和变化

对于稳定和变化,应当有两层次的理解:

首先,开闭原则在宏观设计上的语义告诉我们,已完成的设计都应当是稳定的部分,变化的部分永远是通过增加而不是修改原有设计而完成的。所以我们进行每一部分设计的时候,都应当试图使其成为将来设计的稳定部分,能够进行扩展和兼容。

其次,良好的面向对象的层次设计,应当在每个抽象层次处理自己的逻辑,这一原则要求我们对于新增的变化的部分要进行封装,也即“面向接口(抽象层次)编程,而不要面向具体实现编程。”这要求我们仔细设计和分化“变化”,将变化的部分根据其抽象层次进行分化,使处于同一个抽象层次的变化内容被抽象为一个接口(或其他抽象层次的表述)。

  1. 首次迭代

    自主调度功能通过两个添加实现:调度方法schedule()和其使用的调度优先级计算函数schedulePriority()。服务线程Server增加Buffer用于配合调度方法以及之后的请求重分配。

    电梯重置功能实际上是增加了电梯的一种功能,即增加了电梯的一种状态,以及与这个状态相关的各种路径。因此增加了RESET状态和motionReset()动作,电梯RESET返回的请求可以重用Server Buffer。

  2. 第二次迭代

    双轿厢电梯设计时尽量融入先前的设计,需要调度器和电梯线程配合。首先引入电梯服务区间概念,调度器优先调度符合服务区间的电梯,并使用Best Fit策略;对于没有电梯能够完成的请求,拆分为两个请求并且先后调度。电梯线程仍然只需负责自己的请求,然而同一电梯井内需要完成防碰撞配合,具体分析见下文。

双轿厢避碰:仍是线程同步

在前述设计中,双轿厢电梯都尽量被分割成互不相关的独立的两个运行着的电梯,以最大程度自然地兼容先前的设计思路。然而,实现双轿厢避碰则必然涉及一个电梯阅读另一个电梯的状态,也即线程同步协作问题。

// in reset motion:
int resetTransFloor = command.getResetTransFloor();
if (resetTransFloor != 0) {
    // CHECKME create para elevator and modify self
    ParaLock lock = new ParaLock();
    // create para
    String paraEid = eid + "-B";
    Elevator ev = new Elevator(paraEid, resetTransFloor,
                               elevator.getMaxFloor(), resetTransFloor, command);
    ElevatorThread et = new ElevatorThread(server, ev, paraEid, elevator, lock, command);
    server.addElevator(paraEid, ev, et);
    et.start();
    // modify self
    eid += "-A";
    elevator.setFloor(command.getResetTransFloor() - 1);
    elevator.setEid(elevator.getEid() + "-A");
    paraLock = lock;
    para = ev;
    elevator.setRange(elevator.getMinFloor(), resetTransFloor);
    elevator.setTransFloor(resetTransFloor);
}

// in moving motion:
// if is one of DCE:
if (elevator.getTransFloor() != 0) {
    assert (paraLock != null);
    if (// if moving to transferring floor from neighbouring floor, conflicts possible
        (elevator.isUpperDcElevator() && elevator.atFloor(elevator.getTransFloor() + 1)
         && elevator.nextDirection() == -1)
        ||
        (!elevator.isUpperDcElevator() && elevator.atFloor(elevator.getTransFloor() - 1)
         && elevator.nextDirection() == 1)
    ) {
        Debugger.dbgPrintln("self: conflict, checking para", "elevator thread");
        // will wait on any para conflicts until solved:
        // 1. idle(not moving with direction != STAY) at transFloor
        // 2. moving towards transFloor from neighbouring floor(direction != STAY)
        checkParaConflicts();
    }
}

private void checkParaConflicts() throws InterruptedException {
    assert (para != null);
    // check partner elevator for any conflicts:
    boolean conflict = para.atFloor(para.getTransFloor());
    Debugger.dbgPrintln("cond1=" + conflict, "elevator thread");
    conflict |= (para.atFloor(para.getTransFloor() + 1)
                 && para.nextDirection() == -1);
    Debugger.dbgPrintln("cond12=" + conflict, "elevator thread");
    conflict |= (para.atFloor(para.getTransFloor() - 1)
                 && para.nextDirection() == 1);
    Debugger.dbgPrintln("cond123=" + conflict, "elevator thread");
    while (conflict) {
        // let para go first if conflict
        Debugger.dbgPrintln("para: conflict!", "elevator thread");
        elevator.setDirection(Elevator.Direction.STAY);
        paraLock.waitOn();
        Debugger.dbgPrintln("para: noted!", "elevator thread");
        conflict = para.atFloor(para.getTransFloor());
        Debugger.dbgPrintln("cond1=" + conflict, "elevator thread");
        conflict |= (para.atFloor(para.getTransFloor() + 1)
                     && para.nextDirection() == -1);
        Debugger.dbgPrintln("cond12=" + conflict, "elevator thread");
        conflict |= (para.atFloor(para.getTransFloor() - 1)
                     && para.nextDirection() == 1);
        Debugger.dbgPrintln("cond123=" + conflict, "elevator thread");
    }
}

调度器迭代设计

指定电梯请求分发

private void schedule(PersonRequest request) {
    Elevator elevator = elevators.get(request.getElevatorId() - 1);  // EID starts at 1, so -1
    // This thread ensures that once a new request is acquired from the request
    // queue, it's immediately written to the scheduled elevator's fr_table, so
    // that an elevator can read its scheduled requests in real time.
    elevator.addRequest(request);  // write request in elevator's fr_table
}

从指定到自主调度

private int schedulePriority(Elevator elevator, PersonRequest request) {
    int priority = 0;
    // highest: idle
    if (elevator.getDirection() == Elevator.Direction.STAY) {
        return priority;
    }
    // moving towards:
    // 1st: not full
    if (elevator.isFull()) { return 9999; }
    // 2nd: shorter distance
    priority += 100 * Math.abs(request.getFromFloor() - elevator.getFloor());
    // 3nd: less load
    priority += 10 * elevator.getLoad();
    // 4th: random of [0,10)
    priority += (int) (Math.random() * 10);

    return priority;
}

private void schedule(Request inputRequest) {
    if (inputRequest instanceof PersonRequest) {
        PersonRequest request = (PersonRequest) inputRequest;
        // look in (towards + idle) for an elevator of shortest distance
        int minPriority = 100000;
        int selectedElevatorId = 0;  // from 1 to 6
        for (Elevator elevator : elevators) {
            // elevator under reset, not operable
            if (elevator.getState() == Elevator.State.RESETTING) { continue; }
            // if away from the request, jump it
            // jump on equal because this leads to uncertainty, the req may not get picked up
            boolean jump = elevator.getDirection() != Elevator.Direction.STAY &&
                elevator.getFloor() == request.getFromFloor();
            jump |= elevator.getDirection() != Elevator.Direction.STAY
                && elevator.getFloor() < request.getFromFloor()
                && elevator.nextDirection() == -1;
            jump |= elevator.getDirection() != Elevator.Direction.STAY
                && elevator.getFloor() > request.getFromFloor()
                && elevator.nextDirection() == 1;
            if (jump) { continue; }
            // measure priority and record
            // smaller value of priority means higher priority level!!!
            int priority = schedulePriority(elevator, request);
            // "<"(instead of <=) means if multiple found, take the 1st
            if (priority < minPriority) {
                minPriority = priority;
                selectedElevatorId = elevators.indexOf(elevator) + 1;
            }
        }
        if (minPriority != 100000) {
            // elevator found, start scheduling
            Elevator elevator = elevators.get(selectedElevatorId - 1);// EID starts at 1, so -1
            Debugger.timePrintln(
                String.format("RECEIVE-%d-%d", request.getPersonId(), selectedElevatorId)
            );
            // This thread ensures that once a new request is acquired from the request
            // queue, it's immediately written to the scheduled elevator's fr_table, so
            // that an elevator can read its scheduled requests in real time.
            elevator.addRequest(request);  // write request in elevator's fr_table
        }
        else {  // if all elevator moving away from the req, store req in buffer
            Debugger.dbgPrintln("@Scheduler{}: req jumped", "scheduler");
            bufferThread.addRequest(request);
        }
    } else if (inputRequest instanceof ResetRequest) {
        Debugger.dbgPrintln("@Scheduler{}: rst_req", "scheduler");
        Elevator elevator = elevators.get(((ResetRequest) inputRequest).getElevatorId() - 1);
        elevator.addRequest(inputRequest);
    }
}

public void noteElevatorDirectionChange() {
    bufferThread.setStart(true);
    requestQueue.note();
}

在指定调度模式下,调度器并没有实际意义上的调度功能,因此这一迭代关于自主调度的设计主要分为以下方面:

  1. 调度策略目标:响应时间优先
  2. 调度策略算法设计
  3. 所需配套设施:Server Buffer实现

不使用单例调度器的考虑

兼容双轿厢电梯新需求

private int schedulePriority(Elevator elevator, PersonRequest request) {
    int priority = 0;
    // 5th: random of [0,10)
    priority += (int) (Math.random() * 10);

    // highest: idle
    if (elevator.getDirection() == Elevator.Direction.STAY) {
        return priority;
    }
    // moving towards:
    // 1st: not full
    if (elevator.isFull()) { return 9999; }
    // 2nd: shorter distance
    priority += 1000 * Math.abs(request.getFromFloor() - elevator.getFloor());
    // 3nd: less load
    priority += 100 * elevator.getLoad();
    // CHECKME best fit serving range
    // 4th:
    priority += 10 * elevator.fitRange(request);

    return priority;
}

private void schedule(Request inputRequest) {
    if (inputRequest instanceof PersonRequest) {
        PersonRequest request = (PersonRequest) inputRequest;
        // look in (towards + idle) for an elevator of shortest distance
        int minPriority = 100000;
        String selectedElevatorId = "";
        boolean allReset = true;
        boolean splitRequest = true;
        boolean allBackwards = true;
        synchronized (elevators) {
            for (String eid : elevators.keySet()) {
                Elevator elevator = elevators.get(eid);
                Debugger.dbgPrintln("schedule for " + eid, "scheduler");
                // elevator under reset, not operable
                if (elevator.getState() == Elevator.State.RESETTING) {
                    continue;
                }
                allReset = false;
                // CHECKME check range of request and elevator serving floors
                if (!elevator.containRange(request)) {
                    continue;
                }
                splitRequest = false;
                // if away from the request, jump it
                // jump on equal because this leads to uncertainty, the req may not
                // get picked up
                boolean jump = elevator.getDirection() != Elevator.Direction.STAY &&
                    elevator.getFloor() == request.getFromFloor();
                jump |= elevator.getDirection() != Elevator.Direction.STAY
                    && elevator.getFloor() < request.getFromFloor()
                    && elevator.nextDirection() == -1;
                jump |= elevator.getDirection() != Elevator.Direction.STAY
                    && elevator.getFloor() > request.getFromFloor()
                    && elevator.nextDirection() == 1;
                if (jump) {
                    continue;
                }
                allBackwards = false;
                // measure priority and record
                // smaller value of priority means higher priority level!!!
                int priority = schedulePriority(elevator, request);
                Debugger.dbgPrintln(eid + ": priority=" + priority, "scheduler");
                // "<"(instead of <=) means if multiple found, take the 1st
                if (priority < minPriority) {
                    minPriority = priority;
                    selectedElevatorId = eid;
                }
            }
        }
        if (allReset) {
            Debugger.dbgPrintln("@Scheduler{}: req jumped, all resetting");
            bufferThread.addRequest(request);
        } else if (splitRequest) {
            // CHECKME if no elevator contains the range:
            // TODO pick a floor and split the request
            int transFloor = 0;
            synchronized (elevators) {
                for (Elevator elevator : elevators.values()) {
                    if (elevator.getState() != Elevator.State.RESETTING) {
                        transFloor = elevator.getTransFloor();
                    }
                }
            }
            Debugger.dbgPrintln(
                "@Thread{ServerThread}: picked trans_flr" + transFloor, "server"
            );
            PersonRequest nextRequest = new PersonRequest(
                transFloor, request.getToFloor(), request.getPersonId());
            requestQueue.addRequest(new ParaRequest(
                request.getFromFloor(), transFloor, request.getPersonId(), nextRequest));
        } else if (allBackwards) {
            Debugger.dbgPrintln("@Scheduler{}: req jumped", "scheduler");
            bufferThread.addRequest(request);
        } else {
            synchronized (elevators) {
                // elevator found, start scheduling
                // EID starts at 1, so -1
                Elevator elevator = elevators.get(selectedElevatorId);
                Debugger.timePrintln(
                    String.format(
                        "RECEIVE-%d-%s", request.getPersonId(), selectedElevatorId)
                );
                // This thread ensures that once a new request is acquired from the request
                // queue, it's immediately written to the scheduled elevator's fr_table, so
                // that an elevator can read its scheduled requests in real time.
                elevator.addRequest(request);  // write request in elevator's fr_table
            }
        }
        // End of PersonRequest handling
    } else if (inputRequest instanceof ResetRequest) {
        String eid;
        if (inputRequest instanceof NormalResetRequest) {
            eid = Integer.toString(((NormalResetRequest) inputRequest).getElevatorId());
        } else {
            eid = Integer.toString(((DoubleCarResetRequest) inputRequest).getElevatorId());
        }
        synchronized (elevators) {
            Debugger.dbgPrintln("@Scheduler{}: rst_req", "scheduler");
            Elevator elevator = elevators.get(eid);
            elevator.addRequest(inputRequest);
        }
    }
}

对于双轿厢电梯兼容,调度器主要需要以下功能:

  1. 调度计算函数考虑服务区间
  2. 接收电梯第二次请求回填(无需新增)

调试经验总结与方法论

调试输出器设计

public class Debugger {
    private static final boolean debugOut = false; // debug output switch
    private static final HashMap<String, Boolean> infoSwitch = new HashMap<>();
    private static final String eid = ""; // selected debugging elevator id

    // seperate debug info switch
    static {
        infoSwitch.put("scheduler", false);
        infoSwitch.put("elevator", false);
        infoSwitch.put("elevator thread", true);
        infoSwitch.put("read requests", false);
        infoSwitch.put("server", true);
        infoSwitch.put("reset", false);
        infoSwitch.put("commandlist", true);
        infoSwitch.put("command", true);
        infoSwitch.put("buffer thread", false);
    }

    private static final boolean output = true; // required output switch

    public static void dbgPrintln(Object o) {
        if (debugOut) {
            System.out.println(o);
        }
    }

    public static void dbgPrintln(Object o, String type) {
        if (infoSwitch.get(type)) {
            dbgPrintln(o);
        }
    }

    public static void dbgPrintln(Object o, String type, String selId) {
        if (!eid.isEmpty()) {
            if (eid.equals(selId)) {
                dbgPrintln(o, type);
            }
        } else {
            dbgPrintln(o, type);
        }
    }

    // required output wrapper method
    public static void timePrintln(Object o) {
        if (output) {
            TimableOutput.println(o);
        }
    }
}

调试历史(主要)

INDEXCONTENTDESCRIPTION
1floor index 不匹配使用floor 而非 floor - 1来访问相关数组
2未添加满载相关功能/
3多个运行策略算法实现逻辑错误/
4修复满载功能实现逻辑错误/
5修复服务线程和电梯线程结束条件不正确新增了电梯请求回填后,其要被考虑到结束条件中
6修复重置语句顺序导致充值中电梯被调度开始和结束电梯RESET状态的语句应当在动作的最两侧

心得与体会

线程安全

线程安全是线程安全类的属性,线程同步包含于具有线程同步功能的方法语义所自然说明的。线程安全和同步都应当被封装在类内部完成,以实现对使用者的透明化。

层次化设计

涉及应当建立在抽象层次划分的基础上,每一个模块的涉及应当只处理自己的抽象层次。

...全文
80 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
【重要提示】本资源设置为0积分下载,若非0积分请勿轻易下载 亲爱的CSDN用户: 首先感谢你点进这个资源页面。我需要提前说明一个重要情况: 本资源原本已设置为“0积分下载”,即作者希望完全免费共享。但CSDN平台有时会根据文件的下载热度、文件大小、用户权限等因素,自动将部分资源的积分调整为非0数值(如1积分、2积分、5积分等)。这是平台系统的自动行为,而非作者本人的设定。 因此,如果你当前看到该资源的下载所需积分不是0(例如显示为1、2、3……),请谨慎决定是否下载。 如果你按照非0积分支付并下载后发现资源内容不符合预期、链接失效,或者实际上该资源本应是免费的,作者无法为此承担积分损失或退还操作。强烈建议:仅在页面显示为0积分时进行下载。 另外,本资源描述中并未直接提供具体的下载地址或外部链接,因为它本身是一个通过CSDN官方上传通道提交的文件/内容包。如果你看到描述中没有外部网盘地址,这是正常的——资源文件应通过CSDN内置的“下载”按钮获取。若因平台积分显示异常导致你支付了积分,请优先联系CSDN客服咨询积分退还政策,作者没有权限修改平台自动设定的积分值。 感谢你的理解与支持。技术分享本应开放,但受限于平台规则,特此提醒如上。祝学习进步!
内容概要:本文围绕“计及风电不确定性的电力系统黑启动与负荷恢复协同优化”开展研究,提出了一种融合风电出力随机性与波动性的黑启动及负荷恢复协同优化模型,并基于Matlab平台实现了完整的代码仿真与算例验证。研究构建了考虑不确定性因素的优化框架,通过场景生成与削减技术处理风电出力的随机特征,建立了兼顾系统安全性、恢复效率与供电可靠性的多目标优化模型。文中详细阐述了模型的数学建模过程、关键约束条件(如功率平衡、设备启停顺序、网络拓扑约束等)、求解算法设计及黑启动电源优选策略,并通过标准测试系统验证了所提方法在提升灾后恢复能力方面的有效性与优越性。; 适合人群:具备电力系统分析、优化理论及可再生能源并网基础知识,从事电力系统恢复控制、新型电力系统韧性提升、新能源集成等方向研究的研究生、科研人员及电力行业工程技术人才。; 使用场景及目标:①用于电力系统大面积停电后的黑启动方案制定,优化启动电源选择与负荷恢复时序;②为高比例风电接入背景下电力系统的安全稳定与弹性恢复提供决策支持;③作为Matlab仿真教学资源,辅助理解不确定性建模、随机/分布鲁棒优化及电力系统动态恢复过程。; 阅读建议:建议结合提供的Matlab代码进行同步研读,重点掌握风电不确定性建模方法(如场景法)、优化模型构建逻辑与求解流程(如使用YALMIP调用求解器),宜在熟悉基本电力系统运行与优化算法的基础上进行复现与拓展研究。

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧