|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | import android.content.Context; | 
|---|
|  |  |  | import android.content.Intent; | 
|---|
|  |  |  | import android.content.res.ColorStateList; | 
|---|
|  |  |  | import android.graphics.Color; | 
|---|
|  |  |  | import android.os.Bundle; | 
|---|
|  |  |  | import android.os.Handler; | 
|---|
|  |  |  | import android.os.Message; | 
|---|
|  |  |  | 
|---|
|  |  |  | import android.view.MotionEvent; | 
|---|
|  |  |  | import android.view.View; | 
|---|
|  |  |  | import android.widget.Button; | 
|---|
|  |  |  | import android.widget.RelativeLayout; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | import com.example.agvcontroller.action.ForwardBackAction; | 
|---|
|  |  |  | import com.example.agvcontroller.action.HandInAction; | 
|---|
|  |  |  | import com.example.agvcontroller.action.RotatopnLeftRight; | 
|---|
|  |  |  | import com.example.agvcontroller.protocol.AgvAction; | 
|---|
|  |  |  | import com.example.agvcontroller.protocol.ForceSwitchAction; | 
|---|
|  |  |  | import com.example.agvcontroller.protocol2.PackagePart; | 
|---|
|  |  |  | import com.example.agvcontroller.action.ForceSwitchAction; | 
|---|
|  |  |  | import com.example.agvcontroller.action.HandOutAction; | 
|---|
|  |  |  | import com.example.agvcontroller.socket.NettyServerHandler; | 
|---|
|  |  |  | import com.example.agvcontroller.socket.SocketManager; | 
|---|
|  |  |  | import com.example.agvcontroller.utils.DialogUtil; | 
|---|
|  |  |  | import com.example.agvcontroller.utils.SnowflakeIdWorker; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | import java.net.Socket; | 
|---|
|  |  |  | import java.util.List; | 
|---|
|  |  |  | import java.util.Map; | 
|---|
|  |  |  | import java.util.concurrent.ConcurrentHashMap; | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public class MainActivity extends AppCompatActivity { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static final Map<String, Object> map = new ConcurrentHashMap(); | 
|---|
|  |  |  | public static final Map<String, Object> car_num = new ConcurrentHashMap(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private RecyclerView recyclerView; | 
|---|
|  |  |  | private ItemAdapter itemAdapter; | 
|---|
|  |  |  | private List<Item> itemList; | 
|---|
|  |  |  | private Vibrator vibrator; | 
|---|
|  |  |  | private Button vibrateButton; | 
|---|
|  |  |  | private Button stopBtn;  // 急停按钮 | 
|---|
|  |  |  | //    private Button forceSwitchBtn;  // 手自动强制开关 | 
|---|
|  |  |  | private Button rotatopnLeftBtn;  // 左旋转 | 
|---|
|  |  |  | private Button rotatopnRightBtn; // 右旋转 | 
|---|
|  |  |  | private Button goForwardBtn;  // 前进 | 
|---|
|  |  |  | private Button goBackwardBtn;  // 后退 | 
|---|
|  |  |  | private Button modelSwitchBtn;  // 进入、退出 手动 | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private Button bodySwitchBtn; // 底盘/货叉切换 | 
|---|
|  |  |  | private Socket socket; | 
|---|
|  |  |  | private String AgvNo = "12"; | 
|---|
|  |  |  | private RelativeLayout layout_bottom; | 
|---|
|  |  |  | private RelativeLayout layout_top; | 
|---|
|  |  |  | private RelativeLayout layout_single; | 
|---|
|  |  |  | private RelativeLayout layout_reset; | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | SocketManager socketManager; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private boolean isDowm = false; | 
|---|
|  |  |  | private int autoVal = 1; | 
|---|
|  |  |  | private boolean isAuto = true; | 
|---|
|  |  |  | private boolean isGoForward = false; | 
|---|
|  |  |  | private boolean isGoBackward = false; | 
|---|
|  |  |  | private boolean isRotatopnRight = false; | 
|---|
|  |  |  | private boolean isRotatopnLeft = false; | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private Handler handler = new Handler(new Handler.Callback() { | 
|---|
|  |  |  | @Override | 
|---|
|  |  |  | public boolean handleMessage(Message msg) { | 
|---|
|  |  |  | String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,3); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | AgvAction agvAction = new AgvAction<>(ForceSwitchAction.class) | 
|---|
|  |  |  | .setAgvNo("1") | 
|---|
|  |  |  | .setSerialNo(substring) | 
|---|
|  |  |  | .setVal(1) | 
|---|
|  |  |  | .bodySync((action) -> action.setPwd((short) 21)); | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (isDowm) { | 
|---|
|  |  |  | System.out.println(agvAction); | 
|---|
|  |  |  | //                byte[] message2 = new byte[]{0x01, 0x02, 0x03, 0x06}; // 示例消息 | 
|---|
|  |  |  | if (isGoForward) { | 
|---|
|  |  |  | String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16); | 
|---|
|  |  |  | AgvAction agvAction = new AgvAction<>(ForwardBackAction.class) | 
|---|
|  |  |  | .setAgvNo(AgvNo) | 
|---|
|  |  |  | .setSerialNo(substring) | 
|---|
|  |  |  | .setVal(1) | 
|---|
|  |  |  | .bodySync((action) -> action.setPwd((short) 10)); | 
|---|
|  |  |  | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 | 
|---|
|  |  |  | //                handler.sendEmptyMessageDelayed(0, 100); | 
|---|
|  |  |  | handler.sendEmptyMessageDelayed(0, 100); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (isGoBackward) { | 
|---|
|  |  |  | String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16); | 
|---|
|  |  |  | AgvAction agvAction = new AgvAction<>(ForwardBackAction.class) | 
|---|
|  |  |  | .setAgvNo(AgvNo) | 
|---|
|  |  |  | .setSerialNo(substring) | 
|---|
|  |  |  | .setVal(1) | 
|---|
|  |  |  | .bodySync((action) -> action.setPwd((short) -10)); | 
|---|
|  |  |  | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 | 
|---|
|  |  |  | handler.sendEmptyMessageDelayed(0, 100); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (isRotatopnRight) { | 
|---|
|  |  |  | String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16); | 
|---|
|  |  |  | AgvAction agvAction = new AgvAction<>(RotatopnLeftRight.class) | 
|---|
|  |  |  | .setAgvNo(AgvNo) | 
|---|
|  |  |  | .setSerialNo(substring) | 
|---|
|  |  |  | .setVal(1) | 
|---|
|  |  |  | .bodySync((action) -> action.setPwd((short) 10)); | 
|---|
|  |  |  | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 | 
|---|
|  |  |  | handler.sendEmptyMessageDelayed(0, 100); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (isRotatopnLeft) { | 
|---|
|  |  |  | String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16); | 
|---|
|  |  |  | AgvAction agvAction = new AgvAction<>(RotatopnLeftRight.class) | 
|---|
|  |  |  | .setAgvNo(AgvNo) | 
|---|
|  |  |  | .setSerialNo(substring) | 
|---|
|  |  |  | .setVal(1) | 
|---|
|  |  |  | .bodySync((action) -> action.setPwd((short) -10)); | 
|---|
|  |  |  | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 | 
|---|
|  |  |  | handler.sendEmptyMessageDelayed(0, 100); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | String clientId; | 
|---|
|  |  |  | NettyServerHandler nettyServerHandler; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | @Override | 
|---|
|  |  |  | protected void onCreate(Bundle savedInstanceState) { | 
|---|
|  |  |  | super.onCreate(savedInstanceState); | 
|---|
|  |  |  | setContentView(R.layout.activity_main); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 按钮震动 | 
|---|
|  |  |  | vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); | 
|---|
|  |  |  | vibrateButton = findViewById(R.id.btn_stop); | 
|---|
|  |  |  | stopBtn = findViewById(R.id.btn_stop); | 
|---|
|  |  |  | //        forceSwitchBtn = findViewById(R.id.btn_force_switch); | 
|---|
|  |  |  | goForwardBtn = findViewById(R.id.btn_go_forward); | 
|---|
|  |  |  | goBackwardBtn = findViewById(R.id.btn_go_backward); | 
|---|
|  |  |  | rotatopnLeftBtn = findViewById(R.id.btn_rotatopn_left); | 
|---|
|  |  |  | rotatopnRightBtn = findViewById(R.id.btn_rotatopn_right); | 
|---|
|  |  |  | modelSwitchBtn = findViewById(R.id.btn_model_switch); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | bodySwitchBtn = findViewById(R.id.btn_body_switch); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | layout_bottom = findViewById(R.id.layout_bottom); | 
|---|
|  |  |  | layout_top = findViewById(R.id.layout_top); | 
|---|
|  |  |  | layout_single = findViewById(R.id.layout_single); | 
|---|
|  |  |  | layout_reset = findViewById(R.id.layout_reset); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | Intent intent = getIntent(); | 
|---|
|  |  |  | clientId = intent.getStringExtra("message"); | 
|---|
|  |  |  | Log.i("message1",clientId); | 
|---|
|  |  |  | // 初始化单轴使能 | 
|---|
|  |  |  | int single = 9; | 
|---|
|  |  |  | int len = PackagePart.UNIQUENO.getLen() | 
|---|
|  |  |  | + PackagePart.TIMESTAMP.getLen() | 
|---|
|  |  |  | + PackagePart.COMMAND_MARK.getLen() | 
|---|
|  |  |  | + 16 + 1 + 1 + 1; | 
|---|
|  |  |  | for (int i = 0; i < single; i++) { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte[] message2 = new byte[]{0x01, 0x02, 0x03, 0x06}; // 示例消息 | 
|---|
|  |  |  | nettyServerHandler.sendMessageToClient(clientId, message2); // 发送消息到客户端 | 
|---|
|  |  |  | // 强制模式开关 | 
|---|
|  |  |  | //        forceSwitchBtn.setOnClickListener(new View.OnClickListener() { | 
|---|
|  |  |  | //            @Override | 
|---|
|  |  |  | //            public void onClick(View v) { | 
|---|
|  |  |  | //                if (vibrator != null && vibrator.hasVibrator()) { | 
|---|
|  |  |  | //                    vibrator.vibrate(500); | 
|---|
|  |  |  | //                } | 
|---|
|  |  |  | //                String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16); | 
|---|
|  |  |  | //                AgvAction agvAction = new AgvAction<>(HandOutAction.class) | 
|---|
|  |  |  | //                        .setAgvNo(AgvNo) | 
|---|
|  |  |  | //                        .setSerialNo(substring) | 
|---|
|  |  |  | //                        .setVal(1) | 
|---|
|  |  |  | //                        .bodySync((action) -> action.setPwd((short) 21)); | 
|---|
|  |  |  | //                nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 | 
|---|
|  |  |  | //            } | 
|---|
|  |  |  | //        }); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //vibrateButton.setOnClickListener(new View.OnClickListener() { | 
|---|
|  |  |  | //    @Override | 
|---|
|  |  |  | //    public void onClick(View v) { | 
|---|
|  |  |  | //        if (vibrator != null && vibrator.hasVibrator()) { | 
|---|
|  |  |  | //            //vibrator.vibrate(500); | 
|---|
|  |  |  | //        } | 
|---|
|  |  |  | // | 
|---|
|  |  |  | //        //String clientId = "/192.168.4.188:63160"; | 
|---|
|  |  |  | //        byte[] message1 = new byte[]{0x01, 0x02, 0x03, 0x04}; // 示例消息 | 
|---|
|  |  |  | //        Log.i("message2",clientId); | 
|---|
|  |  |  | //        Log.i("message3", Arrays.toString(message1)); | 
|---|
|  |  |  | // | 
|---|
|  |  |  | //        nettyServerHandler.sendMessageToClient(clientId, message1); // 发送消息到客户端 | 
|---|
|  |  |  | //    } | 
|---|
|  |  |  | //}); | 
|---|
|  |  |  | vibrateButton.setOnTouchListener(new CarTouchButton()); | 
|---|
|  |  |  | // 手自动模式开关 | 
|---|
|  |  |  | modelSwitchBtn.setOnClickListener(new View.OnClickListener() { | 
|---|
|  |  |  | @Override | 
|---|
|  |  |  | public void onClick(View v) { | 
|---|
|  |  |  | if (vibrator != null && vibrator.hasVibrator()) { | 
|---|
|  |  |  | vibrator.vibrate(500); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16); | 
|---|
|  |  |  | if (isAuto) { | 
|---|
|  |  |  | AgvAction agvAction = new AgvAction<>(HandInAction.class) | 
|---|
|  |  |  | .setAgvNo(AgvNo) | 
|---|
|  |  |  | .setSerialNo(substring) | 
|---|
|  |  |  | .setVal(autoVal) | 
|---|
|  |  |  | .bodySync((action) -> action.setPwd((short) 21)); | 
|---|
|  |  |  | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | AgvAction agvAction = new AgvAction<>(HandOutAction.class) | 
|---|
|  |  |  | .setAgvNo(AgvNo) | 
|---|
|  |  |  | .setSerialNo(substring) | 
|---|
|  |  |  | .setVal(autoVal) | 
|---|
|  |  |  | .bodySync((action) -> action.setPwd((short) 21)); | 
|---|
|  |  |  | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | boolean result = false; | 
|---|
|  |  |  | long timestamp = System.currentTimeMillis(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | Log.i("substring",substring); | 
|---|
|  |  |  | while (System.currentTimeMillis() - timestamp < 5000) { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | Object o = map.get(substring); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (null != o) { | 
|---|
|  |  |  | result = true; | 
|---|
|  |  |  | isAuto = !isAuto; | 
|---|
|  |  |  | map.remove(o); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (result) { | 
|---|
|  |  |  | // alert ok  修改按钮样式 名称 | 
|---|
|  |  |  | if (isAuto) { | 
|---|
|  |  |  | modelSwitchBtn.setText("自动模式"); | 
|---|
|  |  |  | autoVal = 0; | 
|---|
|  |  |  | //                        modelSwitchBtn.setBackgroundTintList(ColorStateList.valueOf(Color.RED)); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | autoVal = 1; | 
|---|
|  |  |  | modelSwitchBtn.setText("手动模式"); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | //                    throw new TimeoutException("超时"); | 
|---|
|  |  |  | DialogUtil.showAlertDialog(MainActivity.this, "提示", "请求应答超时!"); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | stopBtn.setOnTouchListener(new CarTouchButton()); | 
|---|
|  |  |  | goForwardBtn.setOnTouchListener(new CarTouchButton()); | 
|---|
|  |  |  | goBackwardBtn.setOnTouchListener(new CarTouchButton()); | 
|---|
|  |  |  | rotatopnLeftBtn.setOnTouchListener(new CarTouchButton()); | 
|---|
|  |  |  | rotatopnRightBtn.setOnTouchListener(new CarTouchButton()); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | @Override | 
|---|
|  |  |  | public boolean onTouch(View view, MotionEvent motionEvent) { | 
|---|
|  |  |  | if (motionEvent.getAction() == KeyEvent.ACTION_DOWN) { | 
|---|
|  |  |  | isDowm = true; | 
|---|
|  |  |  | handler.sendEmptyMessage(0); | 
|---|
|  |  |  | } else if (motionEvent.getAction() == KeyEvent.ACTION_UP) { | 
|---|
|  |  |  | isDowm = false; | 
|---|
|  |  |  | if (view.getId() == R.id.btn_go_forward) { | 
|---|
|  |  |  | // 前进 | 
|---|
|  |  |  | if (motionEvent.getAction() == KeyEvent.ACTION_DOWN) { | 
|---|
|  |  |  | isGoForward = true; | 
|---|
|  |  |  | handler.sendEmptyMessage(0); | 
|---|
|  |  |  | } else if (motionEvent.getAction() == KeyEvent.ACTION_UP) { | 
|---|
|  |  |  | isGoForward = false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } else if (view.getId() == R.id.btn_go_backward) { | 
|---|
|  |  |  | // 后退 | 
|---|
|  |  |  | if (motionEvent.getAction() == KeyEvent.ACTION_DOWN) { | 
|---|
|  |  |  | isGoBackward = true; | 
|---|
|  |  |  | handler.sendEmptyMessage(0); | 
|---|
|  |  |  | } else if (motionEvent.getAction() == KeyEvent.ACTION_UP) { | 
|---|
|  |  |  | isGoBackward = false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } else if (view.getId() == R.id.btn_rotatopn_left) { | 
|---|
|  |  |  | // 左旋转 | 
|---|
|  |  |  | if (motionEvent.getAction() == KeyEvent.ACTION_DOWN) { | 
|---|
|  |  |  | isRotatopnLeft = true; | 
|---|
|  |  |  | handler.sendEmptyMessage(0); | 
|---|
|  |  |  | } else if (motionEvent.getAction() == KeyEvent.ACTION_UP) { | 
|---|
|  |  |  | isRotatopnLeft = false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } else if (view.getId() == R.id.btn_rotatopn_right) { | 
|---|
|  |  |  | // 右旋转 | 
|---|
|  |  |  | if (motionEvent.getAction() == KeyEvent.ACTION_DOWN) { | 
|---|
|  |  |  | isRotatopnRight = true; | 
|---|
|  |  |  | handler.sendEmptyMessage(0); | 
|---|
|  |  |  | } else if (motionEvent.getAction() == KeyEvent.ACTION_UP) { | 
|---|
|  |  |  | isRotatopnRight = false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return false; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private boolean isVisble = true; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 货叉等按钮 | 
|---|
|  |  |  | public void switchover(View view) { | 
|---|
|  |  |  | if (isVisble) { | 
|---|
|  |  |  | layout_bottom.setVisibility(View.GONE); | 
|---|
|  |  |  | layout_top.setVisibility(View.VISIBLE); | 
|---|
|  |  |  | bodySwitchBtn.setText("货叉"); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | layout_bottom.setVisibility(View.VISIBLE); | 
|---|
|  |  |  | layout_top.setVisibility(View.GONE); | 
|---|
|  |  |  | bodySwitchBtn.setText("底盘"); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | isVisble = !isVisble; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | layout_single.setVisibility(View.GONE); | 
|---|
|  |  |  | layout_reset.setVisibility(View.GONE); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 单轴使能按钮 | 
|---|
|  |  |  | public void switchSingle(View view) { | 
|---|
|  |  |  | layout_bottom.setVisibility(View.GONE); | 
|---|
|  |  |  | layout_top.setVisibility(View.GONE); | 
|---|
|  |  |  | layout_single.setVisibility(View.VISIBLE); | 
|---|
|  |  |  | layout_reset.setVisibility(View.GONE); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 复位按钮 | 
|---|
|  |  |  | public void switchRest(View view) { | 
|---|
|  |  |  | layout_bottom.setVisibility(View.GONE); | 
|---|
|  |  |  | layout_top.setVisibility(View.GONE); | 
|---|
|  |  |  | layout_single.setVisibility(View.GONE); | 
|---|
|  |  |  | layout_reset.setVisibility(View.VISIBLE); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | @Override | 
|---|
|  |  |  | protected void onDestroy() { | 
|---|
|  |  |  | super.onDestroy(); | 
|---|
|  |  |  | //SocketManager.getInstance().disconnect(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //SocketManager.getInstance().stopServer(); | 
|---|
|  |  |  | //socketManager.stopServer(); // 停止服务器 | 
|---|
|  |  |  | } | 
|---|